Reputation: 6906
Why is it that when attempting to call delete_all
or destroy_all
on a an instance of ActiveRecord::Associations::CollectionProxy
, and passing it conditions, the ArgumentError: Valid values are :nullify or :delete_all
error is thrown, whereas ActiveRecord::Relation#delete_all
does not?
This comes up when attempting to, for example delete_all
, given a relationship, such as may exist between Movies
and Actors
. In this case, given a Movie
instance, one may want to do something like:
movie.actors.delete_all(id: [2,3,4])
The above does not work, throwing an error.
The same is true for their counterpart #destroy_all
.
Upvotes: 2
Views: 2547
Reputation: 457
Despite the old question I thought I'd drop an answer.
ActiveRecord::Relation
and ActiveRecord::Associations
work differently and can be annoying. They are highly useful though when using destroy to make sure you don't have a DB full of orphans.
That said back to what you were trying to do.
To delete an array of data using active record:
YourApplication::Models::YourClass.destroy_all(selector: value)
DO NOT do this: (This code is incorrect)
YourApplication::Models::YourClass.find(selector: value).destroy_all
To delete an array that has already been returned
data_to_delete = YourApplication::Models::YourClass.find(selector: value).destroy_all
data_to_delete.each(&:destroy)
Back to the specific question
This code is incorrect
movie.actors.delete_all(id: [2,3,4])
The problem here is your using an AR association to return an array of data and then calling .delete_all on that data (Then hoping that the args of delete_all will act as a SQL type WHERE IN. This is sadly not how AR works.)
The correct way to do this
actors.delete_all(args)
Where args will be something like movie: 'titanic', id: [1,2,3]
I hope that helps anyone reading this in the future. Be very careful when using delete with associations as Active Record will auto kill orphan data. Delete is the safer option if your unsure.
Upvotes: 5
Reputation: 6906
Though it seems like they ought to behave identically, and follow the principle of least surprise, actually behave very surprisingly and differently:
ActiveRecord::CollectionProxy#delete_all
and its counterpart #destroy_all
take as its argument a dependent
parameter, whose default value is nil
. This is very different from how ActiveRecord::Relation#delete_all
works, which takes a set of conditions, such as those that may be passed to a subclass of ActiveRecord::Base
.
So, given the Actor
model from above, one may do:
Actor.delete_all(id: [2,3,4])
But, one may not grab a Movie's
associated Actor
records via the associatino proxy and then invoke delete_all
on that.
Upvotes: 2