You can scope a validates_uniqueness_of
call as follows.
validates_uniqueness_of :user_id, :scope => :friend_id
You can use validates
to validate uniqueness
on one column:
validates :user_id, uniqueness: {scope: :friend_id}
The syntax for the validation on multiple columns is similar, but you should provide an array of fields instead:
validates :attr, uniqueness: {scope: [:attr1, ... , :attrn]}
However, validation approaches shown above have a race condition and can’t ensure consistency. Consider the following example:
database table records are supposed to be unique by n fields;
multiple (two or more) concurrent requests, handled by separate processes each (application servers, background worker servers or whatever you are using), access database to insert the same record in table;
each process in parallel validates if there is a record with the same n fields;
validation for each request is passed successfully, and each process creates a record in the table with the same data.
To avoid this kind of behaviour, one should add a unique constraint to db table. You can set it with add_index
helper for one (or multiple) field(s) by running the following migration:
class AddUniqueConstraints < ActiveRecord::Migration
def change
add_index :table_name, [:field1, ... , :fieldn], unique: true
end
end
Caveat : even after you've set a unique constraint, two or more concurrent requests will try to write the same data to db, but instead of creating duplicate records, this will raise an ActiveRecord::RecordNotUnique
exception, which you should handle separately:
begin
# writing to database
rescue ActiveRecord::RecordNotUnique => e
# handling the case when record already exists
end
This can be done with a database constraint on the two columns:
add_index :friendships, [:user_id, :friend_id], unique: true
You could use a rails validator, but in general I recommend using a database constraint.
More reading: https://robots.thoughtbot.com/validation-database-constraint-or-both
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With