Some use case background: I am not using Paperclip for avatars or things like that. I want people to make "submissions" that can contain either a file OR a link, depending on the category of the submission they previously chose (the actual file is being uploaded to a table called Submission Details which has a foreign key to Submissions). Some categories have a "link" type, and some categories have an "image" or a "PDF" type. If they select the link category, that subsequent URL info is stored in a separate column than the image/attachment column.
Here is the code in my model to determine which is which:
def nonlink?
if submission.category.submission_file_type == "Mixed (PDF and Images)" || submission.category.submission_file_type == "Images Only"
return true
else
return false
end
end
So ideally I would want :allow_blank if :nonlink? is true or false in the column validation area of the model.
My first question is, does validates_attachment_presence allow for :allow_blank? If it doesn't, what is the best alternative.
Secondly, what is the syntax for creating an if statement with :allow_blank in a model? Right now I have this but not sure it's right:
validates_attachment_presence :attachment, allow_blank: true, if: [:nonlink? == false]
Would appreciate any thoughts, thanks!
Solved this by doing an extra validation line containing validates_presence_of instead of just validates. I made two separate methods, nonlink and link, with one looking like:
validates_presence_of :attachment, if: :nonlink?
Using this I was able to get rid of :allow_blank entirely.
Related
I'm trying to perform a clean up of some data.
I have details in various forms with various duplicates.
models/object.rb
attr_accessible :name, :email, :assoc_id
I want to merge duplicates where the name is matching and the email is either matching or nil, and the assoc_id is either matching or nil.
Not sure how I write the query to bring back groups of objects that are either matching or nil..
i.e.
grouped_objects = Object.group_by{|o| [o.name]}
brings me grouped just on the name
grouped_objects = Object.group_by{|o| [o.name, o.email]}
brings me grouped on name and email.
the issue is that many of the objects have missing data.
Just want a quick and dirty so that, in the absence of other information, I'll merge the records together.
However, if there's someone with a different email, or a different assoc_id I won't merge that. Appreciate that there'll be some false records, but what we'll end up with will be an improvement
How do I write that activerecord query?
grouped_objects = Object.group_by{|o| [o.name, o.email || o.email == nil]}
Hope that makes sense,
I think a better way is too make your model intolerant with duplication. You can prevent duplication directly in the model. So when your controller try to create an object, it checks before if it doesn't exist by some element you decide.
So if you want your object be unique by some element, better do something like that (assuming you want the uniqueness from name and email field) in MyModel.rb :
class MyModel < ActiveRecord::Base
attr_accessible :name, :email, :assoc_id
validates_uniqueness_of :name
validates_uniqueness_of :email, :allow_nil => true # or :allow_blank => true
# Your code...
end
You can also use :case_sensitive => false if you don't want upper case be differenciated from lower case.
Hope this is what you are looking for !
I have a Rails 3.2.18 app where I'm trying to do some conditional validation on a model.
In the call model there are two fields :location_id (which is an association to a list of pre-defined locations) and :location_other (which is a text field where someone could type in a string or in this case an address).
What I want to be able to do is use validations when creating a call to where either the :location_id or :location_other is validated to be present.
I've read through the Rails validations guide and am a little confused. Was hoping someone could shed some light on how to do this easily with a conditional.
I believe this is what you're looking for:
class Call < ActiveRecord::Base
validate :location_id_or_other
def location_id_or_other
if location_id.blank? && location_other.blank?
errors.add(:location_other, 'needs to be present if location_id is not present')
end
end
end
location_id_or_other is a custom validation method that checks if location_id and location_other are blank. If they both are, then it adds a validation error. If the presence of location_id and location_other is an exclusive or, i.e. only one of the two can be present, not either, and not both, then you can make the following change to the if block in the method.
if location_id.blank? == location_other.blank?
errors.add(:location_other, "must be present if location_id isn't, but can't be present if location_id is")
end
Alternate Solution
class Call < ActiveRecord::Base
validates :location_id, presence: true, unless: :location_other
validates :location_other, presence: true, unless: :location_id
end
This solution (only) works if the presence of location_id and location_other is an exclusive or.
Check out the Rails Validation Guide for more information.
I have two separate forms for a profile picture and the rest of the profile information. Both forms, however, correspond to the profile model. For several of the profile attributes, I have validations like:
validates :title, presence: true
validates :zip_code, presence: true
The problem is that the validations are checked when someone tried to upload an image, which I don't want. That being said, I also have an image validator, so I don't want to avoid validation completely, just certain ones. I was thinking of trying to access the params hash in the model, but I can't figure out how and I'm pretty certain its a bad idea anyway. How can I make the right validation conditions? I already tried this:
validates :title, presence: true, :unless => :picture_exists?
def picture_exists?
if self.pic
puts 'yo pic exist'
return true
else
puts 'yo no pic'
return false
end
end
but it does not work because it checks whether or not the profile has a picture, not whether the params have a picture. So if someone had already saved a picture, they would be able to bypass the validations which I don't want. I want the validations to be bypassed when they are not using the picture submit form.
You can approach the issue in several ways:
1.- Skip all validations in your controller action (and validate manually, I guess)
save(validate: false) (source)
2.- Use a condition that you set manually before saving like this.
3.- Use a custom validation that stops all other validations from triggering if passes.
Maybe you can come up with more.
PS: Why would you expect your user to bypass the other validations before setting the profile picture?
GL & HF
Hi I wan't to validate the unique combination of 3 columns in my table.
Let's say I have a table called cars with the values :brand, :model_name and :fuel_type.
What I then want is to validate if a record is unique based on the combination of those 3. An example:
brand model_name fuel_type
Audi A4 Gas
Audi A4 Diesel
Audi A6 Gas
Should all be valid. But another record with 'Audi, A6, Gas' should NOT be valid.
I know of this validation, but I doubt that it actually does what I want.
validates_uniqueness_of :brand, :scope => {:model_name, :fuel_type}
There is a syntax error in your code snippet. The correct validation is :
validates_uniqueness_of :car_model_name, :scope => [:brand_id, :fuel_type_id]
or even shorter in ruby 1.9.x:
validates_uniqueness_of :car_model_name, scope: [:brand_id, :fuel_type_id]
with rails 4 you can use:
validates :car_model_name, uniqueness: { scope: [:brand_id, :fuel_type_id] }
with rails 5 you can use
validates_uniqueness_of :car_model_name, scope: %i[brand_id fuel_type_id]
Depends on your needs you could also to add a constraint (as a part of table creation migration or as a separate one) instead of model validation:
add_index :the_table_name, [:brand, :model_name, :fuel_type], :unique => true
Adding the unique constraint on the database level makes sense, in case multiple database connections are performing write operations at the same time.
To Rails 4 the correct code with new hash pattern
validates :column_name, uniqueness: {scope: [:brand_id, :fuel_type_id]}
I would make it this way:
validates_uniqueness_of :model_name, :scope => {:brand_id, :fuel_type_id}
because it makes more sense for me:
there should not be duplicated "model names" for combination of "brand" and "fuel type", vs
there should not be duplicated "brands" for combination of "model name" and "fuel type"
but it's subjective opinion.
Of course if brand and fuel_type are relationships to other models (if not, then just drop "_id" part). With uniqueness validation you can't check non-db columns, so you have to validate foreign keys in model.
You need to define which attribute is validated - you don't validate all at once, if you want, you need to create separate validation for every attribute, so when user make mistake and tries to create duplicated record, then you show him errors in form near invalid field.
Using this validation method in conjunction with ActiveRecord::Validations#save does not guarantee the absence of duplicate record insertions, because uniqueness checks on the application level are inherently prone to race conditions.
This could even happen if you use transactions with the 'serializable' isolation level. The best way to work around this problem is to add a unique index to the database table using ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the rare case that a race condition occurs, the database will guarantee the field's uniqueness.
Piecing together the other answers and trying it myself, this is the syntax you're looking for:
validates :brand, uniqueness: { scope: [:model_name, :fuel_type] }
I'm not sure why the other answers are adding _id to the fields in the scope. That would only be needed if these fields are representing other models, but I didn't see an indication of that in the question. Additionally, these fields can be in any order. This will accomplish the same thing, only the error will be on the :model_name attribute instead of :brand:
validates :model_name, uniqueness: { scope: [:fuel_type, :brand] }
I have a url field named link in my model with the following validation
validates_uniqueness_of :link, :case_sensitive => false
When I put "http://stackoverflow.com", it goes well.
Now when I put "https://stackoverflow.com/" (with the trailing slach), this is also accepted as unique.
But I want it to be invalid though there is "/" at the last?
I'd suggest that you normalize your URLs (add/strip trailing slash, etc. see http://en.wikipedia.org/wiki/URL_normalization) before storing them in the DB and even before validation.
validates_uniqueness_of :link, :case_sensitive => false
before_validation :normalize_urls
def normalize_urls
self.link.strip!
self.link.gsub!(/\/$/,'')
end
This isn't quite what you were asking for but if you don't store normalized URLs, you'll have to query your DB for all possible variations during validation and that could quickly get expensive.
You could always do a custom validator (by using the validate method, for example).
It might look something like this:
class MyModel < ActiveRecord::Base
validate :link_is_unique
def link_is_unique
#Clean up the current link (removing trailing slashes, etc)
link_to_validate = self.link.strip.gsub(/\/$/,'')
# Get the current count of objects having this link
count = MyModel.count(:conditions => ['link = ?', link_to_validate])
# Add an error to the model if the count is not zero
errors.add_to_base("Link must be unique") unless count == 0
end
end
You could then add other logic to clean up the link (i.e. check for http://, www, etc.)
You can customize validations. See this railscast.