Validate uniqueness relatively to the parent - ruby-on-rails

I have a post model and a keyword model (with a name attribute), and a post has_many keywords.
I want to validate the uniqueness of keywords, but relatively to its post, not to all keywords.
What I mean is: first_post and second_post can both have the keyword apple, but they can't have it twice. I'd like to avoid the duplication.
If I just add in the keyword model:
validates :name, uniqueness: true
It will check uniqueness of name among all the keywords.
How can I precise that it should only be for its post ?
EDIT:
I did add:
validates :name, uniqueness: { scope: post_id }
to the keyword.rb file.
I now get an error:
undefined local variable or method `post_id' for #<Class:0x007f8fa46b7890>
But my keyword model has a post_id attribute. Any idea on what could be causing this ?

You can use scope.
:scope - One or more columns by which to limit the scope of the
uniqueness constraint.
validates :name, uniqueness: { scope: :post_id }

Related

How to prevent two objects from having the same two identical attributes in Rails? [duplicate]

I have a Release model with medium and country columns (among others). There should not be releases that share identical medium/country combinations.
How would I write this as a rails validation?
You can use a uniqueness validation with the scope option.
Also, you should add a unique index to the DB to prevent new records from passing the validations when checked at the same time before being written:
class AddUniqueIndexToReleases < ActiveRecord::Migration
def change
add_index :releases, [:country, :medium], unique: true
end
end
class Release < ActiveRecord::Base
validates :country, uniqueness: { scope: :medium }
end
All the above answers are missing how to validate the uniqueness of multiple attributes in a model. The code below intends to tell how to use multiple attributes in a scope.
validates :country, uniqueness: { scope: [:medium, :another_medium] }
It validates uniqueness of country in all rows with values of medium and another_medium.
Note: Don't forget to add an index on the above column, this insures fast retrieval and adds a DB level validation for unique records.
Update: For adding an index while creating table
t.index [:country, :medium, :another_medium], unique: true
You can pass a :scope parameter to your validator like this:
validates_uniqueness_of :medium, scope: :country
See the documentation for some more examples.

validate uniqueness of polymorphic association

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

Validate Uniquess With one more condition in Rails

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

Rails validate uniqueness only if conditional

I have a Question class:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id
end
A given user can only create a single question per day, so I want to force uniqueness in the database via a unique index and the Question class via validates_uniqueness_of.
The trouble I'm running into is that I only want that constraint for non-admin users. So admins can create as many questions per day as they want. Any ideas for how to achieve that elegantly?
You can make a validation conditional by passing either a simple string of Ruby to be executed, a Proc, or a method name as a symbol as a value to either :if or :unless in the options for your validation. Here are some examples:
Prior to Rails version 5.2 you could pass a string:
# using a string:
validates :name, uniqueness: true, if: 'name.present?'
From 5.2 onwards, strings are no longer supported, leaving you the following options:
# using a Proc:
validates :email, presence: true, if: Proc.new { |user| user.approved? }
# using a Lambda (a type of proc ... and a good replacement for deprecated strings):
validates :email, presence: true, if: -> { name.present? }
# using a symbol to call a method:
validates :address, presence: true, if: :some_complex_condition
def some_complex_condition
true # do your checking and return true or false
end
In your case, you could do something like this:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id, unless: Proc.new { |question| question.user.is_admin? }
end
Have a look at the conditional validation section on the rails guides for more details: http://edgeguides.rubyonrails.org/active_record_validations.html#conditional-validation
The only way I know of to guarantee uniqueness is through the database (e.g. a unique index). All Rails-only based approaches involve race conditions. Given your constraints, I would think the easiest thing would be to establish a separate, uniquely indexed column containing a combination of the day and user id which you'd leave null for admins.
As for validates_uniqueness_of, you can restrict validation to non-admins through use of an if or unless option, as discussed in http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
Just add a condition to the validates_uniqueness_of call.
validates_uniqueness_of :created_on, scope: :user_id, unless: :has_posted?
def has_posted
exists.where(user_id: user_id).where("created_at >= ?", Time.zone.now.beginning_of_day)
end
But even better, just create a custom validation:
validate :has_not_posted
def has_not_posted
posted = exists.where(user: user).where("DATE(created_at) = DATE(?)", Time.now)
errors.add(:base, "Error message") if posted
end

Rails: validates uniqueness through

I have the following Models:
Language
Itemtype
Item
belongs_to :itemtype
LocalisedItem
belongs_to :item
belongs_to :language
The LocalisedItem model has an attribute called "title".
I want to validate the uniqueness of said "title" attribute. My problem is the scope: It´s supposed to be unique per language (easy) and itemtype, which I could not figure out how to do until now.
My best try...
validates :title, :uniqueness => { :scope => [:language_id, 'item.itemtype_id'] }
...fails with "NoMethodError: undefined method `item.itemtype_id'".
Is there any way to check for uniqueness in the way described?
You can use this format for validate uniqueness with a scope:
validates_uniqueness_of :title, :scope => :language_id

Resources