This project is archived and is in readonly mode.
find_or_create_by doesn't work with protected attributes
Reported by Szymon Nowak | November 16th, 2010 @ 01:16 PM
There's some strange problem with find_or_create_by on has_many
associations and protected attributes:
user.posts.find_or_create_by_some_protected_attribute doesn't
assign "some_protected_attribute", but
user.posts.find_or_initialize_by_some_protected_attribute does.
I'm attaching a failing test case.
Comments and changes to this ticket
-
Szymon Nowak November 16th, 2010 @ 01:19 PM
- no changes were found...
-
caseyf November 16th, 2010 @ 06:27 PM
I'm having this problem as well. No luck tracking it down so far - ActiveRecords's association internals are a mystery to me.
-
caseyf November 16th, 2010 @ 07:05 PM
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...
-
Szymon Nowak November 16th, 2010 @ 08:10 PM
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 November 19th, 2010 @ 01:27 PM
- 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 November 19th, 2010 @ 01:29 PM
- Tag set to 2.3.10, active_record
-
2kan November 19th, 2010 @ 01:50 PM
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.
-
Szymon Nowak November 20th, 2010 @ 03:15 PM
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 November 20th, 2010 @ 03:58 PM
@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 November 20th, 2010 @ 04:07 PM
@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 November 20th, 2010 @ 08:52 PM
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 November 21st, 2010 @ 09:39 PM
- 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_protected
The 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 :)
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>