model relations and bulk deletes - ruby-on-rails

I have a model relation of dependant=>destroy that has to do 50K+ deletes when the destroy is triggered. Looking at the console, rails is trying to do an explicit delete with ID for every single row, which is taking a while. Is there a way for me to force rails to do a bulk delete? Or, I can remove the model dependency, is there a way to do this kind of bulk delete from the code?
Thanks

You should be able to set dependent: delete_all
If you can't get that to work, you might want to use delete_all in your own callback.
To be clear, delete_all should generate a single statement to delete all child objects

Related

Delete all in rails console

i have an association for a user as user has_many agents and agent belongs_to user.
in rails console,i am trying to use different users to test a particular scenario and i want a user with no agents,hence i want to delete the user.agents.
i tried user.agents.map(&:destroy),but it gives error as ActiveRecord::StaleObjectError: Attempted to delete a stale object.i even tried user.agents.delete_all but it too does not work.can i delete the users agents with a single command in rails console.
You better use destroy because it goes through all the Rails magic (callbacks and such)
user.destroy #For a single record
user.agents.destroy_all #For a collection
You are looking for a .destroy_all method. It destroys all records of a given collection.
So user.agents.destroy_all, would return an empty array for user.agents.
You could not have used .delete_all because it is a class method and it deletes records that match a given condition. Like this, Agent.delete_all(condition). If used without a condition it deletes all records from a matched table.
Keep in mind that .destroy methods are instance methods. They instantiate an object and perform callbacks before erasing it. .delete methods are class methods and they directly erase an object.
This works for me
user.agents.find_each(&:destroy)
ActiveRecord::StaleObjectError
Is for Optimistic locking, remove any locks you have on it before trying to delete again. Check if anyone else is using the system or submit any forms you have open.

Deleting objects from associations in Rails

I am learning Rails and I am reading the Beginning Rails 3 book. When you have a has_many association you automatically receives methods.
Let say user has many articles.
user.articles.delete(article)
Now that line only set the foreign key of the article to "NULL". Is it correct that you also must destroy/delete the article if you want it to disappear from the database, or is there a method that does it both?
And what happens if you destroy an article that is in a relationship with a user before you delete the association?
There are some difference between delete and destroy.
The delete method essentially deletes a row.. that's it..
On the other hand, destroy allows you more options:
it will check any callbacks such as before_delete, or any dependencies specified on the model.
it will also keep the object that just got deleted in memory; So it allows you to leave a message saying for example: “Article #{article.id} deleted!!”
And the answer for your question: it will delete any child objects associated with the object.
So, instead of
user.articles.delete(article)
you can use
user.articles.destroy(article)
In this way you will prevent any orphaned rows on the database.

What does User.destroy_all or User.delete_all do?

I am working on a project that has the following cucumber step:
Given /^no registered users$/ do
User.delete_all
end
As a new RoR user this looks a little dangerous even though I'd be testing on our development database because our User table has actual data. What is the line of code doing?
Thanks!
delete_all is from activerecord library not from FactoryGirl.
And the difference between these two is :
delete_all(conditions = nil) public
Deletes the records matching conditions without instantiating the records first, and hence not calling the destroy method nor invoking callbacks.
This is a single SQL DELETE statement that goes straight to the database, much more efficient than destroy_all.
Be careful with relations though, in particular :dependent rules defined on associations are not honored.
Returns the number of rows affected.
destroy_all(conditions = nil) public
Destroys the records matching conditions by instantiating each record and calling its destroy method.
Each object’s callbacks are executed (including :dependent association options and before_destroy/after_destroy Observer methods).
Returns the collection of objects that were destroyed; each will be frozen, to reflect that no changes should be made (since they can’t be persisted).
Note
Instantiation, callback execution, and deletion of each record can be time consuming when you’re removing many records at once. It generates at least one SQL DELETE query per record . If you want to delete many rows quickly, without concern for their associations or callbacks, use delete_all instead.
delete_all is not from FactoryGirl, it is an active record command and it deletes the users from your database. If you are running this from cucumber then it should run against your test database, not development.
A better alternative is destroy_all since that version will run any associated callbacks. For example, if users have posts, and you have a before_destroy callback to remove posts if users are deleted.
Here's a link to more info about delete_all
delete_all will forceably remove records from the corresponding table without activating any rails callbacks.
destroy_all will remove the records but also call the model callbacks
Based on your example, it's probably deleting all users in order to allow the next Cucumber step to register new users. The ActiveRecord::Base#delete_all method says, in part:
Deletes the records matching conditions without instantiating the
records first, and hence not calling the destroy method nor invoking
callbacks. This is a single SQL DELETE statement that goes straight to
the database, much more efficient than destroy_all.
There are probably better ways to write that test, but the intent is clearly to remove the user records as efficiently as possible.
As for it being dangerous, your tests should be running against the test database, not the development or production databases. Since it's possible to misconfigure your testing framework to use the wrong database, you could certainly add a step or conditional that tests if Rails.env.test? is true. That's a fairly small price to pay for peace of mind.

delete_all vs destroy_all?

I am looking for the best approach to delete records from a table. For instance, I have a user whose user ID is across many tables. I want to delete this user and every record that has his ID in all tables.
u = User.find_by_name('JohnBoy')
u.usage_indexes.destroy_all
u.sources.destroy_all
u.user_stats.destroy_all
u.delete
This works and removes all references of the user from all tables, but I heard that destroy_all was very process heavy, so I tried delete_all. It only removes the user from his own user table and the id from all the other tables are made null, but leaves the records intact in them. Can someone share what the correct process is for performing a task like this?
I see that destroy_all calls the destroy function on all associated objects but I just want to confirm the correct approach.
You are right. If you want to delete the User and all associated objects -> destroy_all
However, if you just want to delete the User without suppressing all associated objects -> delete_all
According to this post : Rails :dependent => :destroy VS :dependent => :delete_all
destroy / destroy_all: The associated objects are destroyed alongside this object by calling their destroy method
delete / delete_all: All associated objects are destroyed immediately without calling their :destroy method
delete_all is a single SQL DELETE statement and nothing more. destroy_all calls destroy() on all matching results of :conditions (if you have one) which could be at least NUM_OF_RESULTS SQL statements.
If you have to do something drastic such as destroy_all() on large dataset, I would probably not do it from the app and handle it manually with care. If the dataset is small enough, you wouldn't hurt as much.
To avoid the fact that destroy_all instantiates all the records and destroys them one at a time, you can use it directly from the model class.
So instead of :
u = User.find_by_name('JohnBoy')
u.usage_indexes.destroy_all
You can do :
u = User.find_by_name('JohnBoy')
UsageIndex.destroy_all "user_id = #{u.id}"
The result is one query to destroy all the associated records
I’ve made a small gem that can alleviate the need to manually delete associated records in some circumstances.
This gem adds a new option for ActiveRecord associations:
dependent: :delete_recursively
When you destroy a record, all records that are associated using this option will be deleted recursively (i.e. across models), without instantiating any of them.
Note that, just like dependent: :delete or dependent: :delete_all, this new option does not trigger the around/before/after_destroy callbacks of the dependent records.
However, it is possible to have dependent: :destroy associations anywhere within a chain of models that are otherwise associated with dependent: :delete_recursively. The :destroy option will work normally anywhere up or down the line, instantiating and destroying all relevant records and thus also triggering their callbacks.

Which callback does ActiveRecord use to record timestamp?

I'm just wondering here whether any of you guys know when ActiveRecord use it's "magic" to record the timestamp (e.g. created_at, updated_at).
What i mean when is, at which callback ? (if AR use callback at all).
I'm asking this because I want to create an auto-updating column (that record sequential number for each object) and I want to replicate AR way to do this as much as possible.
EDITED:
It seems that AR does it between after_validation and before_create/before_update. You can do some tests for this by creating a presence validation for created_at column and inserting new record with blank created_at, it would return an error.
I don't know where AR does it, but the proper place for what you describe sounds like before_create
In Rails 3.2.12, this code is located in lib/active_record/timestamp.rb.
As you mention in your question and DGM suggests, Rails will update the timestamps when creating or updating, so sticking your code in before_create and before_update should work.
You may also want to take a look at the ActiveRecord counter_cache functionality. ActiveRecord supports creation of a column that can automatically be incremented/decremented. Additionally, you can perform more complicated logic.

Resources