Rails: validate uniqueness of two columns (together)
Solution 1
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
Solution 2
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
Solution 3
You can pass a :scope
parameter to your validator like this:
validates_uniqueness_of :medium, scope: :country
See the documentation for some more examples.
Related videos on Youtube
Jackson Cunningham
Updated on July 27, 2022Comments
-
Jackson Cunningham almost 2 years
I have a
Release
model withmedium
andcountry
columns (among others). There should not bereleases
that share identicalmedium
/country
combinations.How would I write this as a rails validation?
-
Khoga over 8 yearsPossible duplicate of Rails: Validate uniqueness of multiple columns
-
-
Aleks almost 8 years+1 for the index, but -1 for the
unique
as it is not recognized. For that part I have used the answer below. -
tompave almost 8 yearsYes, sorry, the validation key should be
uniqueness
, notunique
. See the linked documentation. Fixing the answer. -
Aleks almost 8 yearsHm, nice, thanks :) To repeat myself - putting the index brings the solution to the next level, and not just like other "coding" solutions that I have been running into, before finding this answer. +1 for that
-
soupdog over 7 years@DennisBest It "works", but it doesn't protect against race conditions. If two clients make simultaneous requests, they could both pass validation if neither is committed to the database before the other is validated. You also need a database unique constraint as in tompave's answer.
-
Dennis Hackethal over 2 years'Pass' an array to
scope:
to check uniqueness across more than two fields. -
buncis about 2 yearssince the validates is based on 3 column the index table should too t.index [:country, :medium, :another_medium], unique: true