rails: 'validates_uniqueness_of' ignored if 'accepts_nested_attributes_for' is used
When batch creating a number of objects (as is done via mass assignment with accepts_nested_attributes_for
), it looks like validates_uniqueness_of
is just checking the db for conflicts, but not looking for conflicts within the batch currently being saved.
For example, given these two models:
class Parent < ActiveRecord::Base
has_many :children
accepts_nested_attributes_for :children
end
class Child < ActiveRecord::Base
belongs_to :parent
validates_uniqueness_of :name
end
Here’s my output from the console. See that, despite the uniqueness constraint, two children with the same name were created, and that they then can’t be re-saved!
?> p = Parent.create
=> #<Parent id: 3, created_at: "2011-06-08 17:31:54", updated_at: "2011-06-08 17:31:54">
>> p.update_attributes :children_attributes => [{:name => "Kid"}, {:name => "Kid"}]
=> true
>> Child.all.map(&:name)
=> ["Kid", "Kid"]
>> c = Child.first
=> #<Child id: 4, parent_id: 3, name: "Kid", created_at: "2011-06-08 17:32:12", updated_at: "2011-06-08 17:32:12">
>> c.save
=> false
>> c.errors.full_messages
=> ["Name has already been taken"]
I understand it may not be the role of validates_uniqueness_of
to validate these objects against each other, but is there at least a reasonable workaround short of hand-coding a list-sorter/de-duper or something? Thanks!
About this issue
- Original URL
- State: closed
- Created 13 years ago
- Reactions: 4
- Comments: 41 (4 by maintainers)
The issue is still present 8 years later in Rails 6.
https://github.com/toptal/database_validations -> validates_db_uniqueness_of did solve the issue.
Thank you
I have used this patch in my code and it seem works well on Development and Staging Environment. However, on Production I faced some problems but not sure it is related to this patch or not. User add nested records after saved it responded ok but it doesn’t save any data. After users try 2 or 3 times, it saved. It is happen only some time and not often.
Hi, I’d suggest you use validates_db_uniqueness_of. It should solve your problem. Let me know if it’s not. The validator is not completely ready: some of the options are not supported yet but will be very soon.
Any feedback is appreciated 👍
Can we reopen this?
Hi @djezzzl, thanks a lot for your explanation! I’ve already went to this direction to iterate children and retrieve error message from there.
Just got into this problem too
+1 for reopening, a simple iteration / group_by would solve it
@jamesst20 Thank you, glad it helps, more cool staff coming soon 👍
P.S. In case, you work with FactoryBot I can suggest you to try https://github.com/djezzzl/factory_trace And, if you like to have your DB to be consistent with models definition, there is a https://github.com/djezzzl/database_consistency.
@alexander-e1off
Hi, thank you for your case!
First of all, I want to highlight that the gem solves the real problem: now we don’t save duplicates and
save
returns false.About your issue: this is happening because of the implementation of
accepts_nested_attributes_for
. The flow is the following:valid?
on every sub-entity. Eachvalid?
returnstrue
because there is no duplication yet (we didn’t commit the transaction to a database).false
.There is an option (even if a bit ugly) to get the errors:
As you can see, first sub-entity doesn’t have any errors but second does, you can extract it and use.
I hope this explanation helps you.
This just bit me as well. Thankfully, I had an index in place to prevent the “bad” data from being saved. However, it’d make for a much better user experience if the issue could be caught during the ActiveRecord validation cycle.
+1 for reopening;
validates_associated
doesn’t work. To summarize, the issue is that if you:accepts_nested_attribues_for
on that association; andvalidates ..., :uniqueness
on itthen the uniqueness validator:
Likewise,
validates_associated
is of no help since it doesn’t address the underlying uniqueness issue.validates_associated works fine with validates_uniqueness of if you are adding a single record to a collection. Where it breaks is when you use collection.build to add multiple records. If there are duplicates within the new records, the validation doesn’t find them.
I can reproduce the problem, but I don’t think it’s directly related to
nested_atributes_for
. It also happens associating the children manually:You could prevent this behavior with
validates_associated
:Hope that’s what you’re looking for.