Truncate table on migration down method of ActiveRecord Rails 3.1 - ruby-on-rails-3

I have the following defined on my up method on my migration to set initial data:
def up
Color.create!({:id=>1,:name=>"",:color=>"FF6633"})
Color.create!({:id=>2,:name=>"",:color=>"93B233"})
Color.create!({:id=>3,:name=>"",:color=>"4D90D9"})
Color.create!({:id=>4,:name=>"",:color=>"C43092"})
end
Is there any truncate directive I can put on the down method like:
def down
Color.truncate
end
Or since I'm setting the IDs on the create should I use only the destroy_all method of the model Color ?

You can simple use this in your up method, this will solve your both truncate and id resetting problem also.
def up
ActiveRecord::Base.connection.execute("TRUNCATE table_name")
Color.create!({:id=>1,:name=>"",:color=>"FF6633"})
Color.create!({:id=>2,:name=>"",:color=>"93B233"})
Color.create!({:id=>3,:name=>"",:color=>"4D90D9"})
Color.create!({:id=>4,:name=>"",:color=>"C43092"})
end
Cheers!

Firstly, you don't have to pass :id into create! because ActiveRecord will automatically handle that, thus :id likely to get ignored (standard case assumed).
Secondly, it is not a good practice to use ActiveRecord query builder in migration because should the model Color name be changed, you are to have a broken migration. I highly recommend you to use pure SQL and execute that query with execute().
Thirdly, for the #down method, you shouldn't truncate the table. You should destroy those 4 colors that you created in #up.
Here's how I would write it:
def down
colors = ["FF6633", "93B233", "4D90D9", "C43092"]
Color.where(:color => colors).destroy_all
end

Related

How to toggle a boolean field for multiple records generating just one SQL query

I'm trying to write a migration to update a boolean field where the field will mean the exact opposite of what it means currently. Therefore I need to toggle every record to update the records where this field is true to false and vice-versa.
Example:
class ChangePostsVisibleToArchived < ActiveRecord::Migration[6.1]
def change
rename_column :posts, :visible, :archived
# Toggle all tracked topics
# What I DON'T want to do:
Post.all.each { |post| post.toggle! :archived }
end
end
The way I described above will generate one SQL command per Post record.
Is there a way I can toggle all records within a single SQL command using rails ActiveRecord syntax?
Post.update_all "archived = NOT archived"

Sanitize raw SQL with ActiveRecord

I'm trying to get the following request sanitized to send to a MySQL server:
INSERT INTO `table`
SELECT NULL, t.`id`, ?, ?
FROM `table` AS t
WHERE t.`some_field` = ?
The tricky part is that that request is to be executed in a class that is not my model class. It looks like this:
class Model < ActiveRecord::Base
def some_method
Service.new(self).run
end
end
class Service
def initialize(model)
#model = model
end
def run
# Here is the request
end
end
I've seen a lot of people using Model#sanitize_sql, but this is a protected method, which is unusable in my context.
Any idea?
EDIT:
It has been suggested that my question might be a duplicate of this one. I've seen this question before posting, but the answers provided there aren't relevant in my case: I don't want to use quote because most of my fields are going to be numeric values. The other answer suggests not using raw SQL, but, as stated in the comments, I don't think ActiveRecord is capable of generating an INSERT...SELECT query. (This question seems to confirm it)

How to use update_all but each? Is there a better method?

Im current working on a small project and I want to seed my database faster. I migrated a new column called "grand_total_points" to my table of users. So originally I was using this code.
user = User.all
user.each do |x|
x.grand_total_points = x.total_points
x.save!
end
This takes me ages, because I have to update a million record.
Total_points have already been defined in my user model where it calculates all the users points that have been submitted. Forgive me for my explanation. Is there a way to use update_all method but with each included in it?
Yep, possible:
User.update_all('grand_total_points = total_points')
It will generate the following SQL query:
UPDATE "users" SET "grand_total_points" = 'total_points'
If total_points is not a column but an instance method, move the logic into update_all query.
User.update_all("grand_total_points = #{total_points calculation translated into SQL terms}")
I found something that could work. So basically i combine a ruby code with an execute SQL statement, and I put it in a migration file. Here's how the code works. I hope this helps. Make sure you follow the query according to your data.
class ChangeStuff < ActiveRecord::Migration
def change
points = Point.select('user_id, SUM(value) AS value').group(:user_id)
points.each do |point|
execute "UPDATE users SET grand_total_points = #{point.value} WHERE users.id = #{point.user_id}"
end
end
end
You should run bundle exec rake db:migrate after that. The normal way takes me 2-3hours. This only took me 2minutes.

Possible to use ActiveRecord methods against AR collections?

I would like to be able to pull all records from the db:
u = User.all
And then once loaded be able to apply AR methods to the resulting collection:
u.first
Is this possible in rails?
Once you actually query the database, the results become an array instead of an ActiveRecord::Relation. (Though #first would still work fine, since it's a method that also exists on Array).
If you just need a starting point to build an ActiveRecord::Relation though, you can use scoped:
# Doesn't execute a query yet
u = User.scoped
# This now executes a query similar to SELECT * FROM users LIMIT 1
u.first
Note that in Rails 4.0, #all now does the same thing as #scoped (whereas in Rails 3, it returns an array).
Why don't you try it?
User.all doesn't return an AR collection it returns an Array. Get rid of the .all and you will have a working example.

Ruby On Rails: How to run safe updates

I'm using RoR and I want to do concurrency safe update queries. For example when I have
var user = User.find(user_id)
user.visits += 1
I get the following SQL code:
SELECT * FROM Users WHERE ID=1 -- find user's visits (1)
UPDATE Users SET Visits=2 WHERE ID=1 -- 1+1=2
But if there are several queries taking place at the same time, there will be locking problems.
According to RoR API I can use :lock => true attribute, but that's not what I want.
I found an awesome function update_counters:
User.update_counters(my_user.id, :visits => 1)
This gives the following SQL code
UPDATE Users SET Visits=Visits+1 WHERE ID=#something
Works great!
Now my question is how can I override the += function to do the update_counters thing instead?
Because
user.visits += 1 # better
User.update_counters(my_user.id, :visits => 1) # than this
UPDATE
I just created the following function
class ActiveRecord::Base
def inc(column, value)
User.update_counters(self.id, column => value)
end
end
Are there any other better ideas?
Don't know about better ideas, but I would define that method to the User class, not the ActiveRecord. And maybe increment_counter (that uses update_counters) would make it a bit more readable?
def inc(column, value)
self.increment_counter(column, value)
end
Haven't tested that and not saying this is definitely better idea, but that's probably how I'd do it.
Update:
And AFAIK you can't override the "+=", because "a += b" just a shortcut for "a = a + b" and you probably don't want to override "=" to use the update_counters :)