If you want to do it in one call instead of two, you can use the update_all method like this:
Player.where(:grepo_id => a[:grepo_id]).update_all(a)
This will result in the following SQL:
UPDATE players SET ... = ..., ... = ... WHERE players.grepo_id = ...
Also works if the grepo_id doesn't exist: nothing will get updated. Note however that this just runs the SQL; any validations or callbacks on your model are ignored.
If you want to do it in one call instead of two, you can use the update_all method like this:
Player.where(:grepo_id => a[:grepo_id]).update_all(a)
This will result in the following SQL:
UPDATE players SET ... = ..., ... = ... WHERE players.grepo_id = ...
Also works if the grepo_id doesn't exist: nothing will get updated. Note however that this just runs the SQL; any validations or callbacks on your model are ignored.
This is due to you are doing update_attributes even if it does not find the record by grepo_id. find_by_grepo_id returns nil if it does not find any record. So you need to add a condition to get rid of this error.
array.each do |a|
player = Player.find_by_grepo_id(a[:grepo_id])
player.update_attributes(a) if player.present?
end
Howdy all -
In my app I have some resources with the standard CRUD actions. I need an action that can be used in the administrator panel to update all of the records in one hit.
What's best practice here? Is it okay to add a post action in the controller #update_all in addition to the standard CRUD actions? Or is there a more preferred way for rails purists?
update_all updates multiple records in a single query, while update_columns updates only one record. In other words:
User.where(active: true).update_all(deleted_at: nil)
will update all active users, in contrast to update_columns:
User.find(id: 42).update_columns(deleted_at: nil)
which only works for one record.
Therefore, the following will return NoMethodError: undefined method 'update_columns' for #<ActiveRecord::Relation []>:
User.where(active: true).update_columns(deleted_at: nil)
Simply, update_columns expects a hash as a parameter, whereas update_all allows either a string or a hash.
I have a questions variable from another method, where I've already done some filtering. I want to mark these particular questions as active. So I have a method called change_status_to_active. However, when I call the method, it marks every single record in the Question model to status = "A". Why is this?
def change_status_to_active(questions)
puts "MARKING ACTIVE1: #{questions.pluck(:id).to_s}"
originalCount = questions.count
before = Question.where(status: QUESTION_STATUS_ACTIVE).count
questions.update_all(status: QUESTION_STATUS_ACTIVE)
after = Question.where(status: QUESTION_STATUS_ACTIVE).count
puts "Active Orig: #{originalCount.to_s} Before: #{before.to_s} After: #{after.to_s}"
endLook at the output I get...
Active Orig: 26 Before: 0 After: 122
As you can see... questions.count = 26... yet questions.update_all(status: QUESTION_STATUS_ACTIVE) is affecting 122 records.
Why is this?
As of Rails 4.0.2, #update returns false if the update failed. Before Rails 4.0.2, #update returned the object that got updated. The main difference therefore was the return value. After this change, #update_attributes is just an alias of #update.
As of Rails 6.0, #update_attributes is deprecated in favor of #update.
From the rails 5 files it seems to me update can be used to update multiple objects(array of records) but update_attributes only work on single records otherwise both are same
From rails core files for update_attributes:
Updates a single attribute and saves the record. This is especially useful for boolean flags on existing records. Also note that
- Validation is skipped.
- \Callbacks are invoked.
- updated_at/updated_on column is updated if that column is available.
- Updates all the attributes that are dirty in this object.
This method raises an ActiveRecord::ActiveRecordError if the attribute is marked as readonly.
def update_attribute(name, value)
name = name.to_s
verify_readonly_attribute(name)
public_send("#{name}=", value)
save(validate: false)
end
For Update
Updates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
==== Parameters
- +id+ - This should be the id or an array of ids to be updated.
- +attributes+ - This should be a hash of attributes or an array of hashes.
==== Examples
# Updates one record Person.update(15, user_name: "Samuel", group: "expert")
# Updates multiple records people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } } Person.update(people.keys, people.values)
# Updates multiple records from the result of a relation people = Person.where(group: "expert") people.update(group: "masters")
Note: Updating a large number of records will run an UPDATE query for each record, which may cause a performance issue. When running callbacks is not needed for each record update, it is preferred to use {update_all}[rdoc-ref:Relation#update_all] for updating all records in a single query.
def update(id, attributes)
if id.is_a?(Array)
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
object.update(attributes[idx])
}
else
if ActiveRecord::Base === id
raise ArgumentError,
"You are passing an instance of ActiveRecord::Base to `update`. " \
"Please pass the id of the object by calling `.id`."
end
object = find(id)
object.update(attributes)
object
end
end
From the ActiveRecord#update documentation:
people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
Person.update(people.keys, people.values)
So in your case:
updates = {22974 => {column: 2}, 22975 => {column: 33}, 22976 => {column: 94}, 22977 => {column: 32}}
Model.update(updates.keys, updates.values)
Edit: Just had a look at the source, and this is generating n SQL queries too... So probably not the best solution
The only way I found to do it is to generate INSERT INTO request with updated values. I'm using gem "activerecord-import" for that.
For example, I have a table with val values
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
| pkey | id | site_id | feature_id | val | created_at | updated_at |
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
| 1 | | 125 | 7 | 88 | 2016-01-27 10:25:45 UTC | 2016-02-05 11:18:14 UTC |
| 111765 | 0001-0000024 | 125 | 7 | 86 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:18:14 UTC |
| 111766 | 0001-0000062 | 125 | 7 | 15 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:18:14 UTC |
| 111767 | 0001-0000079 | 125 | 7 | 19 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:18:14 UTC |
| 111768 | 0001-0000086 | 125 | 7 | 33 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:18:14 UTC |
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
select records
products = CustomProduct.limit(5)
update records as you need
products.each_with_index{|p, i| p.val = i}
save records in single request
CustomProduct.import products.to_a, :on_duplicate_key_update => [:val]
All you records will be updated in single request. Please find out gem "activerecord-import" documentation for more details.
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
| pkey | id | site_id | feature_id | val | created_at | updated_at |
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
| 1 | | 125 | 7 | 0 | 2016-01-27 10:25:45 UTC | 2016-02-05 11:19:49 UTC |
| 111765 | 0001-0000024 | 125 | 7 | 1 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:19:49 UTC |
| 111766 | 0001-0000062 | 125 | 7 | 2 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:19:49 UTC |
| 111767 | 0001-0000079 | 125 | 7 | 3 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:19:49 UTC |
| 111768 | 0001-0000086 | 125 | 7 | 4 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:19:49 UTC |
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
Easiest thing to do is simply multiple SQL queries, it's a one off migration so no biggie I think. ActiveRecord update_all is meant to update the matching records with the same value so that won't work.
Glass.all.find_each do |glass|
glass.update!(created_at: glass.prescription.created_at)
end
If you want one query (update based on a join - called "update from" in sql terms) it seems not straightforward in ActiveRecord (should work on MySQL but not on Postgres) https://github.com/rails/rails/issues/13496 it will be easier to write raw SQL - this can help you get started https://www.dofactory.com/sql/update-join
You can use touch method
Prescription.find_each do |prescription|
prescription.glass.touch(:created_at, time: prescription.created_at)
end