In Rails, how can I validates_uniqueness_of: field with a volume of the last 6 months

First element
I want to check a field to make sure it is unique (for the last 6 months) before storing it in the database.

I think I should use validates_uniqueness_of: field, case_sensitive => false, Scope => ...

For my application, it must be unique if it was used <6 months ago.

I think to compare it with created_at, but I don’t know how to do it.

Second element
I think I should somehow use .strip to remove any spaces before or after text that might be used by chance (I know that these extra spaces are used by default in rails, and if they exist, they can do unique entry).

If anyone has any hints on how this should be done correctly, I would really appreciate it.

+4
source share
2 answers

validates_uniqueness_of works by checking if a record with the same value of a given field exists within a given scope. :scope allows you to define the scope of uniqueness; for example, if I was creating blog software and only wanted one post title for one blog, I could say validates_uniqueness_of :title, :scope => :blog_id - no frame, I would only allow each title to be used once per the whole system. :scope will not allow you to perform complex validation like what you want.

What you probably need to do is create your own validation function to verify the uniqueness of the field in question for a given time interval (the code is included in the model):

 validate :field_must_be_unique_within_six_months def field_must_be_unique_within_six_months return if field.blank? num_duplicates = self.class.count(:conditions => ["field = ? AND created_at < ?", self.field, 6.months.ago]) if num_duplicates > 0 errors.add(:field, :taken) end end 

The field_must_be_unique_within_six_months method will work similarly to validates_uniqueness_of , because it will add an error message if there is already an entry with the same field specified, but with the added condition that it also checks the date. validate :field_must_be_unique_within_six_months will add the method to the validation process when the record is saved.

To check multiple fields at the same time without violating DRY, you can use validates_each to do something like the following:

 validates_each :field1, :field2 do |record, attr, value| if record.class.exists?(["#{attr.to_s} = ? AND created_at < ?", value, 6.months.ago]) errors.add(attr, :taken) end end 

In the record block above, the record checked, attr is the attribute (therefore field1 , field2 , etc.), and value is the value of this attribute.

+10
source

Perhaps you can do something like this:

 def validate errors.add(:field, 'blah blah') if is_used_recently && !has_unique_field? end def has_unique_field? Model.exists?(['field = ? and created_at > ?', self.field, 6.months.ago]) end def is_used_recently self.created_at < 6.months.ago || self.new? # i don't know if created_at would be set by this point end 

Alternatively, you can create a new validation handler or extend an existing one to pass a parameter: inside, if that's what you are going to do often.

To get rid of the leading and trailing spaces, you need a strip. You can run this in all of your fields by doing something like:

 before_validation :clean_up_whitespace def clean_up_whitespace self.some_field.strip! # this does the strip in place end 

I hope this helps, let me know if I made any mistakes!

+3
source

Source: https://habr.com/ru/post/1285928/


All Articles