In Rails / Activerecord I have changed a field to make it required; I want to run
AppVersion.where('content_rating IS NULL').each {|av| av.update_column('content_rating', 7) }
to ensure that content_rating is not null.
From what I read, Migrations are not a good place to actually change records. Is there a "do this once" way to run code within the Rails structure?
Yes, you can create a rake task:
http://railsguides.net/2012/03/14/how-to-generate-rake-task/
$ rails g task update_version update_rating_column
$ create lib/tasks/update_version.rake
namespace :update_version do
desc "Update content_rating"
task :update_rating_column => :environment do
AppVersion.where('content_rating IS NULL').each {|av| av.update_column('content_rating', 7) }
end
end
You can run the task in the migration if needed:
Execute a Rake task from within migration?
Related
I've written the spec to test my migration but when I run it I got an error:
ActiveRecord::PendingMigrationError:
Migrations are pending. To resolve this issue, run:
bin/rake db:migrate RAILS_ENV=test
I've tried to disable the migration check in the before section but that check is running before all tests.
How to disable the migration check for testing purposes?
Testing Rails migration is a bit of a pain so I would rather step back and think about if this needs to be in a Rails migration / tested in a Rails migration.
There are basically two different types of migrations
Schema migrations
Use mostly Rails built in functions. Unless you do some handcrafted SQL I wouldn't bother testing this and trust the framework here.
Data migrations
Data migrations are used to backfill or change data. As data is one of your most valuable assets and loosing or corrupting it is very painful I would definitely recommend to write tests for data migrations.
As mentioned, testing migrations is a bit of a pain so I would try to abstract the data migration code in it's own (service) class. Something like
class DataMigration::UpdateUsername
def self.run
new.run
end
def run
User.all do |batch|
user.update(name: user.name.capitalize)
end
end
end
You can now test the data migration like a normal class like this:
it 'does capitalize the name' do
user = create(:user, name: 'name')
DataMigration::UpdateUsername.run
expect(user.reload.name).to eq('NAME')
end
Now we can use this class in our Rails migration or e.g. just use it in a Rake task. Using it in a Rake task also has the advantages that we can pass in parameters, run several data migrations in parallel (e.g. you have a large data set) or even in a background job which you can't in a Rails migration.
Example
class DataMigration::UpdateUsername
def initialize(start_id:, finish_id:)
#start_id = start_id
#finish_id = finish_id
end
def run
User.find_in_batches(start: start_id, finish: finish_id) do |batch|
batch.each do |user|
user.update(name: user.name.capitalize)
end
end
end
end
Now we can create a custom task for this
namespace :db do
desc "Runs user data migration"
task :update_user, [:start, :finish] do |task, args|
DataMigration::UpdateUsername.new(start_id: args[:start], finish_id: args[:finish])
end
end
rake db:update_user[0, 10000]
rake db:update_user[10000, 20000]
# ...
In config/environments/test.rb add the line
config.active_record.migration_error = false
I am trying to modify my database in rails for that I want to write a script that would modify my model accordingly when executed. I have no idea how to create a script and run it using rails console. Please somebody guide me.
Eg -: Suppose i want to write a script that has Model.all written in it and when I execute it using console Model.all should run
Use Rails tasks instead:
lib/tasks/mytasks.rake
namespace :mytasks do
desc "This is a Hello world task. All it does it say hello"
task :hello => :environment do
puts "Hello!"
end
end
Then in the console you do:
rake mytasks:hello
One the quick temporary solution adds a new class method to your Model:
class Model
def self.modify_db
Model.find_each do |item|
end
end
end
In console just type: Model.modify_db
I need to update a table data in database using RAILS migrations.
Sample:
Table: Table_A(Col_A(number), Col_B(varchar),...)
Query: UPDATE Table_A SET Col_B = "XXX" where Col_B = "YYY"
What would be the best way to do this using RAILS Migrations. I am not even sure if RAILS migrations is the way to go for updating data in database. Any explanation would be helpful.
It's usually better to do these sorts of big data updates in a rake task. I usually write them so they have two versions: rake change_lots_of_data:report and rake change_lots_of_data:update. The 'report' version just executes the where clause and spits out a list of what would be changed. The 'update' version uses the very same where clause but makes the changes.
Some advantages of doing it this way are:
Migrations are saved for changing the database structure
You can run the 'report' version as often as you want to make sure the right records are going to be updated.
It's easier to unit test the class called by the rake task.
If you ever need to apply the same criteria to make the change again, you can just run the rake task again. It's possible but trickier to do that with migrations.
I prefer to do any database data changes in a rake task so that's it's
Obvious
Repeatable
Won't later be executed via rake db:migrate
The code:
namespace :update do
desc "Update table A to set Col_B to YYY"
task :table_a => :environment do
TableA.where(Col_B: "YYY").update_all(Col_B: "XXX")
end
end
end
Then you can rake update:table_a to execute the update.
This should be done in a rake task...
namespace :onetime do
task :update_my_data => :environment do
TableA.where(Col_B: "YYY").update_all(Col_B: "XXX")
end
end
Then after you deploy:
rake onetime:update_my_data
At my company we delete the contents of the onetime namespace rake task after it's been run in production. Just a convention for us I guess.
More details about the update_all method: http://apidock.com/rails/ActiveRecord/Relation/update_all
You can do like this:
class YourMigration < ActiveRecord::Migration
def up
execute('UPDATE Table_A SET Col_B = "XXX" where Col_B = "YYY"')
end
def down
end
end
Or:
class YourMigration < ActiveRecord::Migration
def up
update('UPDATE Table_A SET Col_B = "XXX" where Col_B = "YYY"')
end
def down
end
end
ActiveRecord::Base.connection.execute("update Table_A set Col_B = 'XXX' where Col_B = 'YYY')
I have a Model (let's call it A) in a Rails project that checks an attribute (let's call it a) with the ActiveRecord::Dirty a_changed? function on before_save. I want to be able to save an instance of A in a Rake task, but simply including :environment isn't cutting it--I'm getting a "no method a_changed? defined on A" message in the Rake task. How do I get ActiveRecord to remember about ActiveRecord::Dirty within a Rake task?
Rails version is 2.3.11
namespace :some_namespace do
namespace :some_subnamespace do
desc "This is a Rake Task"
task :some_taskname, [:some_arg] => [:environment] do |t,arg|
foo = A.find(11111)
foo.save #<=== fails with "no method a_changed? defined on A"
end
end
end
Since that's a pretty dense bunch of info, here's the breakdown:
I have a model A with an attribute a.
Model A has a before_save trigger defined that calls a_changed?, which is a method added by ActiveRecord::Dirty in the Rails environment. There are no problems calling this from a controller.
In my Rake task, however, the a_changed? call in the before_save trigger causes a NoMethodError exception to be raised, presumably because the [:environment] requirement is not sufficient to include ActiveRecord::Dirty. My question is how to make this not happen (my workaround is to rescue NoMethodError from inside the before_save, which is an obvious hack).
Looks like your question has already been answered on a previous question asked on StackOverflow.
In order to determine what methods your object has you can do this:
...
desc "This is a Rake Task"
task :some_taskname, [:some_arg] => :environment do |t, args|
foo = A.find(11111)
p foo.methods
...
This will print out a list of the available methods. If the array includes :some_attr_changed? (where some_attr is an attribute), then you can be certain that ActiveRecord::Dirty is indeed working fine in the rake task. If those methods don't show up in the array, then your assumptions are correct.
I'm following this tutorial: http://friendlyorm.com/
I'm using InstantRails to run MySQL locally. To run Ruby and Rails, I'm using normal Windows installations.
When I run Friendly.create_tables! I only get an empty Array returned: => [] and no tables are created in my 'friendly_development' database.
Author of Friendly here.
You'll have to require all of your models before calling Friendly.create_tables! Otherwise, there's no way for Friendly to know which models exist. In a future revision, I'll automatically preload all your models.
I have a rake task, with help from a guy called Sutto, that will load in all your models and then call Friendly.create_tables! and print out all the tables involved.
namespace :friends do
desc "load in all the models and create the tables"
task :create => :environment do
puts "-----------------------------------------------"
Dir[Rails.root.join("app", "models", "*.rb")].each { |f|File.basename(f, ".rb").classify.constantize }
tables = Friendly.create_tables!
tables.each do |table|
puts "Table '#{table}'"
end
puts "-----------------------------------------------"
end
end
rake friends:create
not much to go on here. My guess is that it can't find your model file that you are creating in the path?