-
Notifications
You must be signed in to change notification settings - Fork 21.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changed ActiveRecord::Relation#update behavior so that it will work on Relation objects without giving id #11898
Conversation
def update(id, attributes) | ||
if id.is_a?(Array) | ||
id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) } | ||
if attributes.is_a?(Array) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably simpler:
id.map.with_index do |one_id, idx|
attrs = attributes.is_a?(Array) ? attributes[idx] : attributes
update(one_id, attrs)
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@egilburg Thanks. Updated :)
This implementation issues a single query per record to update. Since all the records are updated with the same condition we can perform the update in a single query. Is there really a need for this PR, when you could use: Person.where(id: [1,2,3,4]).update_all(last_seen: Time.now) |
Isn't |
Not that I know of => https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation.rb#L304 But you need to call it on a |
@senny |
@prathamesh-sonpatki yea right. I just browsed the API and it seems there is no method that acts on the @rafaelfranca @carlosantoniodasilva is there a reason we only have |
Investigated it a bit. Right now if you try to call I think we have 3 usecases
|
@prathamesh-sonpatki I'm not sure we need 2) if we have 3). Let's wait for some feedback. |
@carlosantoniodasilva Any thoughts? |
any updates on this? |
@carlosantoniodasilva @senny @rafaelfranca 3 month ping. |
I have rebased with current master |
I prefer to have 3) and not implement 2). @prathamesh-sonpatki could you take a look if it is possible to implement 3)? |
@rafaelfranca Its possible. i will update soon |
@rafaelfranca Sorry for the delay. I have updated. Please take a look |
@rafaelfranca Can you see this issue? |
Sure, but I don't want to include this on 4.1, so we will have to wait the final release to merge it |
@rafaelfranca any updates on this issue? |
# # Updates multiple records from the result of a relation | ||
# people = Person.where(group: 'expert') | ||
# people.update(group: 'masters') | ||
def update(id = nil, attributes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this will work. If you pass only one argument it will be always id
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure about 1.9, but just checked: it's fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. Same with 1.9.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.. that said, I think I'd rather that .update(nil, group: 'masters')
continued to fail, instead of suddenly meaning "update all the things".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. This make sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is the best name. But .update(nil, group: 'masters')
updating all the things can be a huge problem in existing applications upgrading to 4.2. Maybe we should check if id
is a hash and trigger this new behaviour.
Also I was thinking. Depending on the number of record your relation return you may have a huge performance issue since it will do a update query for each record.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes, maybe defaulting the id value to :all
would fix this concern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rafaelfranca For performance issues, should we do find_each
instead of to_a
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would have an improvement to not load all the record on the memory but the number of queries would be even worse. I think we should only document this possible performance problem and point users to update_all
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rafaelfranca I updated the PR. Should we also update ActiveRecord getting started guide where we mention update
and update_all
I really not sure about this. @dhh @jeremy @tenderlove WDYT? |
I’m personally a big fan of this. I really don’t like the #update_all thing. And this strikes me as a far more welcome syntax. So 👍 from my end. On Jun 4, 2014, at 4:19 PM, Rafael Mendonça França notifications@github.com wrote:
|
+1 Aaron Patterson
|
This seems (IMO) confusingly close to |
If you want callbacks to fire, then you have to do N+1. I think this is a legit use case, it’s neatly readable, and we document clearly what’s going on. On Jun 4, 2014, at 8:49 PM, Matt Jones notifications@github.com wrote:
|
If update_all fires no callbacks, perhaps it should be deprecated in favor of update_columns. update_all is ambiguous and actually a bit scary since the name may imply "all as in all records in db, as opposed to current relation scope" Also, should similar change be made for Relation#delete? Eugene
|
Agree. We should make that clearer. And uniform across the API. So #delete should follow #update in the same way. On Jun 4, 2014, at 9:07 PM, Eugene Gilburg notifications@github.com wrote:
|
So, I think this can go in, to round out:
And then we separately consider whether the |
IMO In an ideal world the strategy would be determined by common arguments and not separate methods .create .save, .update, .destroy, including the whether on record, assoc, or relation, would all take following arguments: validate: true/false Both would default to true if not set. Bang methods like save! should also honor the arguments and only raise if save failed WITH given arguments, rather than always assume save! requires validation. This way a whole bunch of methods can be deprecated. The choice of n+1 or single query should be implementation detail which Rails can do in most efficient way possible under given circumstances. Mist users just want things to work and not have to research which method out of a myriad of similar looking ones is the right one. Eugene
|
Example of how this could clean API up: .update_all => .update .update_attributes => .update(validate: false) .update_columns => .update(validate: false, callbacks: false) Let users say what they want done, not how to do it. Ditto for all the other things... Eugene
|
@rafaelfranca Build is 💚 |
👍 |
Agree that we shouldn’t mix the methods that update records one at the time with callbacks with the bulk SQL update commands. These are so different in strategy that we shouldn’t try to pave over that. On Jun 4, 2014, at 9:41 PM, Eugene Gilburg notifications@github.com wrote:
|
ebbdd2b
to
c548fc1
Compare
c548fc1
to
f87d1fd
Compare
…th callbacks and validations - Right now, there is no method to update multiple records with validations and callbacks. - Changed the behavior of existing `update` method so that when `id` attribute is not given and the method is called on an `Relation` object, it will execute update for every record of the `Relation` and will run validations and callbacks for every record. - Added test case for validating that the callbacks run when `update` is called on a `Relation`. - Changed test_create_columns_not_equal_attributes test from persistence_test to include author_name column on topics table as it it used in before_update callback. - This change introduces performance issues when a large number of records are to be updated because it runs UPDATE query for every record of the result. The `update_all` method can be used in that case if callbacks are not required because it will only run single UPDATE for all the records.
f87d1fd
to
5ef713c
Compare
@rafaelfranca Rebased with latest master. Can we take a look at this now? |
Changed ActiveRecord::Relation#update behavior so that it will work on Relation objects without giving id Conflicts: activerecord/CHANGELOG.md
@rafaelfranca Thanks :) |
Since 9ac7dd4, class level `update`, `destroy`, and `delete` were placed in the `Persistence` module as class methods. But `Relation#update` without passing ids which was introduced at #11898 is not a class method, and it was caused the extra scoping regression #33470. I moved the relation method back into the `Relation` to fix the regression. Fixes #33470.
Since 9ac7dd4, class level `update`, `destroy`, and `delete` were placed in the `Persistence` module as class methods. But `Relation#update` without passing ids which was introduced at #11898 is not a class method, and it was caused the extra scoping regression #33470. I moved the relation method back into the `Relation` to fix the regression. Fixes #33470.
Since 9ac7dd4, class level `update`, `destroy`, and `delete` were placed in the `Persistence` module as class methods. But `Relation#update` without passing ids which was introduced at #11898 is not a class method, and it was caused the extra scoping regression #33470. I moved the relation method back into the `Relation` to fix the regression. Fixes #33470.
That ability was introduced at rails#11898 as `Relation#update` without giving ids, so the ability on the class level is not documented and not tested. c83e30d which fixes rails#33470 has lost two undocumented abilities. One has fixed at 5c65688, but I missed the ability on the class level. Removing any feature should not be suddenly happened in a stable version even if that is not documented. I've restored the ability and added test case to avoid any regression in the future. Fixes rails#34743.
No description provided.