I have a user model with a friends column of type text. This migration was ran to use the array feature with postgres:
add_column :users, :friends, :text, array: true
The user model has this method:
def add_friend(target)
#target would be a value like "1234"
self.friends = [] if self.friends == nil
update_attributes friends: self.friends.push(target)
end
The following spec passes until I add user.reload
after calling #add_friend
:
it "adds a friend to the list of friends" do
user = create(:user, friends: ["123","456"])
stranger = create(:user, uid: "789")
user.add_friend(stranger.uid)
user.reload #turns the spec red
user.friends.should include("789")
user.friends.should include("123")
end
This happens in development as well. The model instance is updated and has the new uid in the array, but once reloaded or reloading the user in a different action, it reverts to what it was before the add_friend
method was called.
Using Rails 4.0.0.rc2 and pg 0.15.1
What could this be?
If you want to use Postgresql array type, you'll have to comply with its format. From Postgresql docs the input format is
which is not what
friends.to_s
will return. In ruby:That is, brackets instead of braces. You'll have to do the conversion yourself.
However I'd much rather rely on ActiveRecord serialize (see serialize). The database does not need to know that the value is actually an array, that's your domain model leaking into your database. Let Rails do its thing and encapsulate that information; it already knows how to serialize/deserialize the value.
Note: This response is applicable to Rails 3, not 4. I'll leave here in case it helps someone in the future.
I suspect that ActiveRecord isn't noticing that your
friends
array has changed because, well, the underlying array reference doesn't change when you:That will alter the contents of the array but the array itself will still be the same array. I know that this problem crops up with the postgres_ext gem in Rails3 and given this issue:
I'd expect Rails4 to behave the same way.
The solution would be to create a new array rather than trying to modify the array in-place:
There are lots of ways to create a new array while adding an element to an existing array, use whichever one you like.
It looks like the issue might be your use of
push
, which modifies the array in place.I can't find a more primary source atm but this post says: