In my model, I would like to add a validation to check if my attribute presents both of its values (e.g., one of the record has its value ValueA and another record has its value ValueB-those being the only possibilities, and it needs to have at least one or both of them).
What is the best way to achieve this?
Try out this
class YourModel < ActiveRecord::Base
VALID_VALUES = ['Value1', 'Value2']
with_options presence: true do
validates :your_field, inclusion: { in: VALID_VALUES, allow_blank: true }
end
end
Related
I've found plenty of posts around how to validate a field is present, if another condition is true, such as these:
Rails: How to validate format only if value is present?
Rails - Validation :if one condition is true
However, how do I do it the opposite way around?
My User has an attribute called terms_of_service.
How do I best write a validation that checks that the terms_of_service == true, if present?
You're looking for the acceptance validation.
You can either use it like this:
class Person < ApplicationRecord
validates :terms_of_service, acceptance: true
end
or with further options, like this:
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { message: 'must be abided' }
end
[edit]
You can set the options you expect the field to be as well, as a single item or an array. So if you store the field inside a hidden attribute, you can check that it is still "accepted" however you describe accepted:
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { accept: ['yes', 'TRUE'] }
end
I can't think of any default validation methods that could serve your purpose, but you can do with a custom validation. Also, a boolean can be either truthy or falsy, so you just need to check if its true or not. something like this should work.
validate :terms_of_service_value
def terms_of_service_value
if terms_of_service != true
errors.add(:terms_of_service, "Should be selected/True")
end
end
I have a model "User" with attribute "Username". Can I use validations to prevent a User being created with the Username "home"?
class User < ActiveRecord::Base
validates :username, presence: true
end
You can use an exclusion validator:
class User < ActiveRecord::Base
USERNAME_BLACKLIST = ['home'].freeze
validates :username, presence: true, exclusion: { in: USERNAME_BLACKLIST }
end
Alternatively, you can always rely on a custom validation method, using validate instead of validates, for more complex types of validation that aren't easily expressed using built-in validators:
class User < ActiveRecord::Base
validates :username, presence: true
validate :username_not_on_restricted_list
protected
def username_not_on_restricted_list
errors.add(:username, :invalid) if username == 'home'
end
end
You could also write a custom validator if you intend to reuse this functionality across multiple models.
Let's say I have a before_validation that checks to make sure the first letter of a name is "y":
before_validation :check_name_first_letter_is_y
But I want to make sure that the first name is also present:
validates :name, presence: true
But this will run the before_validation BEFORE validating if there's a first name present, right? How would I check if a name is present before running my before_validate?
I could try:
after_validation :check_name_first_letter_is_y
But will that stop the save method if I return false? Or is it too late because it's already been validated?
It might be easier to do this in one call as follows (if I didn't misread your question!):
validates :name, presence: ->(rec) { rec.name.initial == 'Y' }
Update:
Introduce a new ActiveModel::EachValidator that checks the first character in the name to be y and use the presence validator as you normally would, but ensure presence validator comes before the name validator so that presence check is done before checking first letter.
# app/validators/NameValidator.rb
class NameValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if value.initial != 'y'
record.errors[attribute] << (options[:message] || "First letter in name must be `y`")
end
end
end
Then in your model, use the following two validations:
validates :name, presence: true
valdiates :name, name: true
Please refer: http://api.rubyonrails.org/classes/ActiveModel/Validator.html.
Also would suggest you to come up with a "Railsy" name for NameValidator class!
You can simply add a second format validation:
validates :name, presence: true, format: { with: /^y/, allow_blank: true }
I'm trying to implement a validation for a polymorphic association, where I only want it to trigger on a certain type. Which is user.
I'd want something like this:
validates :room_id, uniqueness: { scope: tokenable_id if tokenable type is User }
how do I go about doing this. The other type is Customer. Which I want to allow the opportunity to be several.
I think you can use Conditional Validation
Sometimes it will make sense to validate an object only when a given
predicate is satisfied. You can do that by using the :if and :unless
options, which can take a symbol, a string, a Proc or an Array. You
may use the :if option when you want to specify when the validation
should happen. If you want to specify when the validation should not
happen, then you may use the :unless option.
5.1 Using a Symbol with :if and :unless
You can associate the :if and :unless options with a symbol
corresponding to the name of a method that will get called right
before validation happens. This is the most commonly used option.
class Order < ActiveRecord::Base
validates :room_id, uniqueness: { scope: :tokenable_id }, if: :is_right_type?
# or maybe this will work
validates :room_id, uniqueness: { scope: :tokenable_id }, if: :user?
def is_right_type?
type == "user"
end
end
I have a table to enter Vehicle Names, model name is Vehicle
I dont want name to repeat..so i have
validates_uniqueness_of :make, :case_sensitive => false
in my model. Thing is I have used soft delete to delete a record by setting is_deleted flag field to true. This is_deleted column is also present in Vehicle model.
So the problem is if I delete 1 table it just soft-deletes it and when I try to create one vehicle with same name which was soft-deleted then error occures because of validation as the field is not originaly deleted from DB.
Is there a simple way to solve this issue.
I believe this should do the trick:
validates_uniqueness_of :make, :case_sensitive => false, unless: :is_deleted
Conditions
From the sounds of it, you'll want to use a conditional validation, which will look up the name with the constraints of it not having is_deleted? attribute set to true:
#app/models/your_model.rb
Class YourModel < ActiveRecord::Base
validates :make, uniqueness: { scope: :name, message: "Only One Name Allowed Sorry!" }, if: :is_unique?
def is_unique?
return Model.find_by({name: name, is_deleted: false}) #-> will look up any data records which have is_deleted as false & same name
end
end