I asked a similar question here at Uniqueness validation in database when validation has a condition but my requirements have changed, hence this question.
Using uniqueness validations in Rails is not safe when there are multiple processes unless the constraint is also enforced on the database (in my case a PostgreSQL database so see http://robots.thoughtbot.com/the-perils-of-uniqueness-validations).
In my case, the uniqueness validation is conditional: it should only be enforced if another attribute on another model becomes true. So I have
class Parent < ActiveRecord::Base
# attribute is_published
end
class Child < ActiveRecord::Base
belongs_to :parent
validates_uniqueness_of :text, if: :parent_is_published?
def parent_is_published?
self.parent.is_published
end
end
So the model Child
has two attributes: parent_id
(association with Parent
) and text
(a text attribute). The model Parent
has one attribute: is_published
(a boolean). text
should be unique across all models of type Child
iff its parent.is_published
is true.
Using a unique index as suggested in http://robots.thoughtbot.com/the-perils-of-uniqueness-validations is too constraining because it would enforce the constraint regardless of the value of is_published.
Is anyone aware of a "conditional" index on a PostgreSQL database that depends on another table? The solution at Uniqueness validation in database when validation has a condition is when your condition depends on an attribute on the same table. Or another way to fix this?
is_published
attribute was in the same table you could use a partial unique index, but not if it's in another table. I think you're going to have to do whole-table write locking (LOCK TABLE ... IN EXCLUSIVE MODE
) and enforce the constraint client-side. – Craig Ringerchild.is_published
, a multi-column fk and a partial unique index should do the job reliably. – Erwin Brandstetter