Rails: set params but do not save [duplicate] - ruby-on-rails

This question already has answers here:
Rails update_attributes without save?
(4 answers)
Closed 6 years ago.
What's the call to update a Rails record with new params, say, stored in a hash variable? This:
#user.update(hash)
Will save the record, and since I want to put the call in a callback I don't want to save it, just prepare it to be saved correctly in the callback.

You can use attributes= to set the attributes but not save the record.
#user.attributes = hash
New attributes will be persisted in the database when the object is saved. See http://apidock.com/rails/ActiveRecord/AttributeAssignment/attributes

You can do:
#user.attributes = hash
or
#user.assign_attributes hash
Keep in mind that neither of these will return the object you're working on. If you want that, try Object#tap:
#user.tap { |u| u.assign_attributes hash }

Related

Append hash to array column in rails [duplicate]

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?
I suspect that ActiveRecord isn't noticing that your friends array has changed because, well, the underlying array reference doesn't change when you:
self.friends.push(target)
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:
String attribute isn't marked as dirty, when it changes with <<
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:
update_attributes friends: self.friends + [ target ]
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:
One important thing to note when interacting with array (or other mutable values) on a model. ActiveRecord does not currently track "destructive", or in place changes. These include array pushing and poping, advance-ing DateTime objects. If you want to use a "destructive" update, you must call <attribute>_will_change! to let ActiveRecord know you changed that value.
If you want to use Postgresql array type, you'll have to comply with its format. From Postgresql docs the input format is
'{10000, 10000, 10000, 10000}'
which is not what friends.to_s will return. In ruby:
[1,2,3].to_s => "[1,2,3]"
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.

What is the difference between "update_attributes!" and "update_attributes"? [duplicate]

This question already has answers here:
When do I use save!, create! and update_attributes! in Rails?
(3 answers)
Closed 4 years ago.
In my case, update_attributes is not updating the instance with the latest value but update_attributes! is updating the new value.
update_attributes and update_attributes! are aliases of update and update! https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update_attributes-21
The difference between update and update! is right there on the docs https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update-21
Updates its receiver just like update but calls save! instead of save, so an exception is raised if the record is invalid and saving will fail.

How do I mass update a field for models in an array?

I’m using Rails 4.2.7. How do I mass update a field of an array of my models without actually saving that information to the database? I tried
my_objcts_arr.update_all(my_object: my_object)
but this results in the error
NoMethodError: undefined method `update_all' for #<Array:0x007f80a81cae50>
I realize I could iteraet over the array and update each object individually, but I figure there's a slicker, one-line way in Ruby taht I'm not aware of.
update_all needs to be called on a class level active record model/relation, ie User or TaxReturn. Here is one somewhat related SO post showing some examples, and here is the api doc for update_all. It will send the UPDATE directly to the db (it is an active record method, after all), so it is not what you want.
You're best off iterating and updating the value yourself with collect or something similar, which is only one line.
foo = [{:a=>"a", :b=>"b"}, {:a=>"A", :b=>"B:}]
// => [{:a=>"a", :b=>"b"}, {:a=>"A", :b=>"B"}]
foo.collect{|x| x[:a]="C"}
// => ["C", "C"]
foo
// => [{:a=>"C", :b=>"b"}, {:a=>"C", :b=>"B"}]

Why do these array operations not save in ActiveRecord? [duplicate]

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?
I suspect that ActiveRecord isn't noticing that your friends array has changed because, well, the underlying array reference doesn't change when you:
self.friends.push(target)
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:
String attribute isn't marked as dirty, when it changes with <<
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:
update_attributes friends: self.friends + [ target ]
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:
One important thing to note when interacting with array (or other mutable values) on a model. ActiveRecord does not currently track "destructive", or in place changes. These include array pushing and poping, advance-ing DateTime objects. If you want to use a "destructive" update, you must call <attribute>_will_change! to let ActiveRecord know you changed that value.
If you want to use Postgresql array type, you'll have to comply with its format. From Postgresql docs the input format is
'{10000, 10000, 10000, 10000}'
which is not what friends.to_s will return. In ruby:
[1,2,3].to_s => "[1,2,3]"
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.

Increment counter and rails "first" using postgreSQL strange behavior [duplicate]

This question already has answers here:
ActiveRecord Find All not sorting by ID?
(5 answers)
Closed 8 years ago.
When I increment some integer column using increment_counter and passing some record id and then try to get the first record using Model.first, this return the record id plus 1.
Something like this:
Model.increment_counter :field, id
Model.first
It returns not the
Model.find(1)
but
Model.find(id+1)
Is that some particular issue of postgreSQL?
Model.first will use the default sorting of your database (which is not necessarily an id).
Try this instead:
Model.order("id").first
You can do some monkey patching to ActiveRecord,
#lib/postgresql_extras.rb
module ActiveRecord
class Base
def self.first_by_id
order(:id).first
end
def self.all_by_id
order(:id)
end
end
end
and require this in some initializer
#config/initializer/extensions.rb
require "postgresql_extras"
don't call this ones first and all cause it will generate errors on other querys, for example User.order(:email).limit(1) it will be different from User.order(:email).first in this case, cause it will reorder by id the items,
I didn't find other methods with problems in posgresql yet and i try to fix it by change the tables pkey, but not luck there

Resources