Rails calling Models in the Migration - ruby-on-rails

I have a migration that creates a table, and I need to access the model for that table to create another table. The migration seems to not recognized that the original table has been created, so to make sure I put a debugger in my code and when I get to the model call, it says User(Table doesn't exist) even though in mysql I see it being created.
Looks like migrations can't see the current state of the database, any ideas how to get around that?
Just to be more specific about my question:
I'm trying to use Archivist to create archive of my current User table so I have
class ArchivedHeuristicReviewsTable < ActiveRecord::Migration
def self.up
create_table "users" do |t|
t.string "name"
...
end
debugger
Archivist.update User
end
def self.down
drop_table :users
drop_table :archived_users
end
end
the Archivist, doesn't create the archived_user table, so when I stopped at debugger and did User, I got User(Table doesn't exist).
I even tried Archivist call in a newer migration, so to make sure User creation is all done, but it still didn't recognize the user table.
Any ideas?

This should do the trick:
User.connection.schema_cache.clear!
User.reset_column_information

Related

Modifying migration column not loaded in migration rails

I added two migration which in the first one I add a column to a model, and in the second one, I execute a function that stores a value into the column recently added of some rows.
The problem is that when I run rake db:migrate the second migration throws an error because the first migration was loaded but the database hasn't changed yet, so one approach is to run the command twice (it works).
First Migration :
class AddRegisteredReportToSpecialOfferUse < ActiveRecord::Migration
def up
add_column :special_offer_uses, :registered_report, :boolean, default: false
end
def down
remove_column :special_offer_uses, :registered_report
end
end
Second Migration :
class CreateReportsFromMigrations < ActiveRecord::Migration
def change
OneClass.perform
end
end
the OneClass.perform is a method that makes an update of the attribute added previously
def perform
´´´
special_offer_uses.update_attribute(:registered_report, true)
´´´
end
The error thrown :
StandardError: An error has occurred, all later migrations canceled:
undefined method `registered_report=
Note that the method undefined is the name of the attribute added previously.
I wonder if there is a way to avoid running the command twice without throwing any error.
UPDATE:
I found a solution using reset_column_information method that causes the columns to be reloaded on the next request.
Resets all the cached information about columns, which will cause them to be reloaded on the next request.
The most common usage pattern for this method is probably in a migration, when just after creating a table you want to populate it with some default values
Further information : link
Why not do it all in one migration?
class AddRegisteredReportToSpecialOfferUse < ActiveRecord::Migration
def up
add_column :special_offer_uses, :registered_report, :boolean, default: false
OneClass.perform
end
def down
remove_column :special_offer_uses, :registered_report
end
end

Rails Postgres migration without downtime

Let's say I want to add a column on my users table running the following migration
class AddVersionHistoryToUsers < ActiveRecord::Migration
def change
add_column :users, :versions, :string, array: true, default: '{}'
User.find_each do |user|
if user.app_version?
user.versions << user.app_version.to_s
user.save!
end
end
end
end
My aim is to insert the current app_version of each user into the versions array. How can I execute a migration without a lock in the users table due to the default value?
To wrap this up: Looping and querying the database for every User is highly inefficient. You should use update_all for these tasks.
And since your data already exists in the same table you can simply get it from there.
User.where.not(app_version: nil).update_all('versions = ARRAY[app_version]')

Rolling back a migration in Ruby on Rails

I am working on my first ruby on rails project, and have created a "Users" table for my app using a "rails generate scaffold users" command.
Now I am trying to undo this statement as I wish to try to rewrite the classes involved with that class and table in my database.
I saw that the scaffold statement created a "def change" class in a migration, and when I try to rollback that migration there is no function within the migration. I added a "def down" method with only the "drop_table :users" defined within it.
def down
drop_table :users
end
However, when I run "rake db:rollback", there is no response in the command prompt and the table is unchanged.
I am not quite sure how to undo this migration to rewrite the table schema. Can anyone offer assistance please?
The whole migration looks like the following:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.string :password_digest
t.string :email
t.timestamps
end
end
def down
drop_table :users
end
end
EDIT:
Fixed this by running db:drop and db:rollback to clear my schema. I think there was an issue with me having the database open in another program. I closed it and that allowed the database to be rolled back.
rails generate scaffold users doesn't change your database, it only generate MVC code. You can simply undo it by destroy scaffold command
rails destroy scaffold users
If you run rake db:migrate command than you can rollback it by rake db:rollback command. At migration file, change method is enough for create or drop operation of tables. No down method is needed, down method is the complement method of up method.

Renaming original migration file in Rails after doing the rename_table_migration

I happened to create a Query model in Rails and recently found out that this is one of the reserved words now..
I renamed the table using a new migration file and renamed all the files that were created (name of new model - Plot)
Question: is it OK to rename the original migration file (20111228212521_create_queries.rb) to 20111228212521_create_plots.rb)
and everything inside the old file:
class CreateQueries < ActiveRecord::Migration
def change
create_table :queries do |t|
t.string :name
t.text :content
t.timestamps
end
end
end
to
class CreatePlots < ActiveRecord::Migration
def change
create_table :plots do |t|
t.string :name
t.text :content
t.timestamps
end
end
end
??
I just don't want too many migration files and also worried that there may be some errors when I switch to production..
You can change the migration file name, but you have to perform a few steps:
rake db:rollback to the point that queries table is rolled back.
Now change the name of migration file, also the contents.
Change the name of any Model that may use the table.
rake db:migrate
The short answer is to just make another migration file.
Migration files are meant to keep track of each and every change to the database. So, you're encouraged to make small one-off changes in a separate file. I can't speak for your situation, but in my situation, when I make a mistake like this, I simply create a new migration file and don't check the old migration file into source control. This way the errant changes are only on my local db and don't get into prod/dev/staging.
Aside from rolling back, and especially useful for when you need to rename a migration from early on in production you can now in Rails 4 create a new migration to rename it.
$rails generate migration RenamesFooBarr
and then in the method of the new migration add
rename_table :old_migration_name, :new_migration name
like this:
class RenamesFooBar < ActiveRecord::Migration
def change
rename_table :old_foo_bar_name, :new_foo_bar_name
end
end
This will effectively take care of all the indexes as well in the up and down since ActiveRecord recognizes the rename_table
source: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
Simply, do like this
Rollback the migration for that queries. i.e rake db:rollback
Change migration file, class name and the content of it.
Finally, Migrate the database. i.e rake db:migrate

Migrating Data in a Rails Migration

Let's say you have "lineitems" and you used to define a "make" off of a line_item.
Eventually you realize that a make should probably be on its own model, so you create a Make model.
You then want to remove the make column off of the line_items table but for every line_item with a make you want to find_or_create_by(line_item.make).
How would I effectively do this in a rails migration? I'm pretty sure I can just run some simple find_or_create_by for each line_item but I'm worried about fallback support so I was just posting this here for any tips/advice/right direction.
Thanks!
I guess you should check that the Make.count is equal to the total unique makes in lineitems before removing the column, and raise an error if it does not. As migrations are transactional, if it blows up, the schema isn't changed and the migration isn't marked as executed. Therefore, you could do something like this:
class CreateMakesAndMigrateFromLineItems < ActiveRecord::Migration
def self.up
create_table :makes do |t|
t.string :name
…
t.timestamps
end
makes = LineItem.all.collect(:&make).uniq
makes.each { |make| Make.find_or_create_by_name make }
Make.count == makes.length ? remove_column(:line_items, :make) : raise "Boom!"
end
def self.down
# You'll want to put logic here to take you back to how things were before. Just in case!
drop_table :makes
add_column :line_items, :make
end
end
You can put regular ruby code in your migration, so you can create the new table, run some code across the old model moving the data into the new model, and then delete the columns from the original model. This is even reversible so your migration will still work in both directions.
So for your situation, create the Make table and add a make_id to the lineitem. Then for each line item, find_or_create with the make column on lineitem, setting the returned id to the new make_id on lineitem. When you are done remove the old make column from the lineitem table.

Resources