This project is archived and is in readonly mode.
find_or_create_by doesn't work with protected attributes
-
caseyf
I'm having this problem as well. No luck tracking it down so far - ActiveRecords's association internals are a mystery to me.
-
caseyf
It looks like find_or_create through an association calls create_by with a Hash:
https://github.com/rails/rails/commit/f967b352d2abdb88b704266cdb06b......and attributes are protected when creating from Hashes: https://github.com/rails/rails/commit/f967b352d2abdb88b704266cdb06b...
-
caseyf
ack. Ignore me, I'm looking in the wrong place.
-
Szymon Nowak
I'll take a look at it over the weekend, but the workaround that I'm using for now is find_or_initialize_by + save.
-
2kan
- Tag cleared.
I don't think that it is a bug (for 2.3.10 and 3.x). Here: https://github.com/rails/rails/blob/v2.3.10/activerecord/lib/active... says that protected attributes won't be set unless they are given in a block.
Also the same says about find_or_initialize_ so if it works for such methods it is a bug.
-
2kan
- Tag set to 2.3.10, active_record
-
2kan
Also I wrote a test for 2.3.10 and 3.0.3 for find_or_initialize_ and it also fails as it should be. So I don't think that there is any bug.
-
caseyf
Thanks 2kan.
(This is a change in behavior from 2.2.x)
-
Szymon Nowak
First of all it's not a change in behavior from 2.2.x, but from 2.3.5 - I migrated my app from 2.3.5 directly to 2.3.10 and it stopped working.
Second thing is that find_or_initialize_by_id works fine. Here's code from my app:
r = current_user.relationships.find_or_initialize_by_contact_id(1) => #<Relationship id: nil, user_id: 15, contact_id: 1, blocked: false, created_at: nil, updated_at: nil, inverse_id: nil, state: "unapproved", requested_at: nil, approved_at: nil> r.save => true r = current_user.relationships.find_or_create_by_contact_id(1) WARNING: Can't mass-assign these protected attributes: contact_id => #<Relationship id: nil, user_id: 15, contact_id: nil, blocked: false, created_at: nil, updated_at: nil, inverse_id: nil, state: "unapproved", requested_at: nil, approved_at: nil>so the behavior is inconsistent.
-
Szymon Nowak
@2kan: thanks for the link! It turns out that the bug is actually that find_or_initialize_by works with protected attributes in 2.3.10, although it shouldn't :)
-
2kan
@Szymon Nowak I will check again how find_or_initialize_by works in 2.3.10 to be sure that there is a bug and gonna try to patch it so.
-
2kan
Hm! Look here: https://github.com/rails/rails/blob/v2.3.10/activerecord/test/cases... Funny, isn't it? I think that it is a bug, I think that find_or_initialize_by should work as find_or_create_by and as it says in docs, shouldn't it?
But look here: https://github.com/rails/rails/blob/v2.3.10/activerecord/test/cases...
Isn't it messy? I was really surprised reading the code and tests. I can write any patch doing something with it but i have no idea how must it work. Maybe we just should not touch this.
-
Szymon Nowak
- Assigned user set to Aaron Patterson
Ok, so to sum it all up:
The docs for both methods (https://github.com/rails/rails/blob/v2.3.10/activerecord/lib/active... and https://github.com/rails/rails/blob/v2.3.10/activerecord/lib/active...) state that:
Protected attributes won't be set unless they are given in a block.However, in the tests ((https://github.com/rails/rails/blob/v2.3.10/activerecord/test/cases...) the behavior is different:
test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected test_find_or_create_from_one_attribute_should_set_attribute_even_when_protectedThe other thing is how both methods work with has_many associations:
r = current_user.relationships.find_or_initialize_by_contact_id(1) => #<Relationship id: nil, user_id: 15, contact_id: 1> r.save => true r = current_user.relationships.find_or_create_by_contact_id(1) WARNING: Can't mass-assign these protected attributes: contact_id => #<Relationship id: nil, user_id: 15, contact_id: nil>While all the links above point to files in 2.3.10 release, it seems that the same mess is in 3.0.2 release. I hope that someone from the Rails core team can decide how it should actually work :)
