Why is this migration irreversible? (change_table, rename, text) - ruby-on-rails

I have what I think is a pretty simple migration. For some reason I get an IrreversibleMigration error when I try to db:rollback or db:migrate:redo.
The migration runs smoothly, but I'd rather keep it reversible. I can't figure out why it's not as written. Any ideas?
Here's the migration:
class AddWhyHypAndWhyHypeToStatements < ActiveRecord::Migration
def change
change_table :statements do |t|
t.rename :description, :why_hypocritical
t.text :why_hypothetical
end
end
end
If it matters, "description" column is a text column. I'm using Rails 3.1/Ruby 1.9.2/PostgreSQL. Thanks for any help.

Looks like Rails has troubles reverting change_table method. Try doing it that way instead:
class AddWhyHypAndWhyHypeToStatements < ActiveRecord::Migration
def change
rename_column :statements, :description, :why_hypocritical
add_column :statements, :why_hypothetical, :text
end
end
You can see the list of commands that can be inverted in the docs or in Rails Guides.

Related

Change the column in table in ruby on rails

I have a migration file called [timestamp]_create_posts.rb.
I found that I made the column with a wrong data type. I need to make t.text :content instead of t.string :content.
I include the code from the above file:
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.string :content
t.timestamps null: false
end
end
end
I kinda know that I should not directly change the file but rollback migration and change the schema and do the migration again. But I wasn't sure whether that's a right way to do it. It would be really nice if someone can guide me through this. I'm not really familiar with Rails.
You should not rollback anything. You should create a new migration, that will change a column with following content:
class UpdatePostsChangeContentColumn < ActiveRecord::Migration
def change
change_column :posts, :content, :text, limit: 60000 # or whatever
end
end
or, even better, to supply the reasonable rollback of this migration:
class UpdatePostsChangeContentColumn < ActiveRecord::Migration
def up
change_column :posts, :content, :text, limit: 60000 # or whatever
end
# back to previous version
def down
change_column :posts, :content, :string
end
end
The other option if you are just in the development is just to Drop you database and recreate it. Then you can modify that file, but THIS IS NOT ADVISED. I have done this again in development where i didn't care about recreating my database
rake db:drop
rake db:create
rake db:migrate
There are two ways to change the column in table:
You can use rake db:rollback VERSION=file_version and then after that you can change datatype manually.
Generate another migration file. e.g rails g migration RemoveColumnToPost and the you can add following codes:
def up
change_column :posts, :content, :text
end
def down
change_column :posts, :content, :string
end
And then use rake db:migrate

Why are the methods up and down not being created with my subclass of ActiveRecord::Migration?

I'm reading Rails 3 in Action and following the commands verbatim. However, when I run the commands
rails new things_i_bought
cd things_i_bought
bundle install
rails generate scaffold purchase name:string cost:float
The book says I should get this code:
class CreatePurchases < ActiveRecord::Migration
def self.up #not created in my code
create_table :purchases do |t|
t.string :name
t.float :cost
t.timestamps
end
end
def self.down # not created in my code
drop_table :purchases
end
end
I get this code instead:
class CreatePurchases < ActiveRecord::Migration
def change
create_table :purchases do |t|
t.string :name
t.float :cost
t.timestamps
end
end
end
Why are the class methods up and down not being created for me? I'm using
rails 3.1.1 and ruby 1.9.2.
thanks for reading my book!
As JacobM and dbalatero have already explained, this is a new feature in Rails 3.1. This particular feature was added by Aaron Patterson as a way to simplify the migration syntax. In earlier versions of Rails, you would have to do as the book shows:
class CreatePurchases < ActiveRecord::Migration
def self.up
create_table :purchases do |t|
t.string :name
t.float :cost
t.timestamps
end
end
def self.down
drop_table :purchases
end
end
But that's kind of repeating yourself. Aaron created a migration syntax that looks good and is simpler, calling only the methods necessary for migrating forward, but also allowing the migrations backwards (known as a "rollback") too. The same migration written with the Rails 3.1 syntax is this:
class CreatePurchases < ActiveRecord::Migration
def change
create_table :purchases do |t|
t.string :name
t.float :cost
t.timestamps
end
end
end
So when this migration runs "forwards", Rails will create the purchases table with the fields. When you roll it back (or run it "backwards") then Rails will know to drop the table.
This syntax isn't entirely perfect however, and you'll run into problems with methods such as change_column. When that happens, it's best to stick with defining both the def up and def down methods in the migrations:
class CreatePurchases < ActiveRecord::Migration
def up
change_column :purchases, :cost, :integer
end
def down
change_column :purchases, :cost, :float
end
end
That's because in this example Rails won't know how to switch it back to the previous type. I hope this explains it better!
This is a new feature in Rails 3.1. For changes that Rails can figure out how to reverse, such as creating a table, you simply create a "change" method with the code that would have gone in "up", and it figures out how to do "down" on it's own.
You can also define "up" and "down" methods yourself -- for some changes (such as dropping a column) Rails won't be able to figure it out -- but the syntax is a bit different; it's not just def up instead of def self.up (they're now instance methods instead of class methods).
I believe in the new Rails 3.1, the database migration methods are self-aware about how to run an up/down migration.
Therefore, if you define a def change method, it will try to use those self-aware methods: in this case, create_table knows to do DROP TABLE in a down context, and CREATE TABLE in an up context.
If you want the old style, you can probably keep using it and define your own self.down and self.up methods as the book describes.
Edit: here's a link to the blog post on this, called "Reversible Migrations": http://www.edgerails.info/articles/what-s-new-in-edge-rails/2011/05/06/reversible-migrations/index.html

Undefined add_column in Rails 3

I am trying to run a migration in Rails 3, I wish to add a column to a table, the code looks like this:
class AddConstAdr < ActiveRecord::Migration
def change
change_table: constants do |t|
t.add_column :home_address, :string
end
end
end
When I do rake db:migrate I get an error saying undefined method 'add_column'. I am confused as to why this is happening, can anyone help?
You seem to be mixing two different ways of doing a migration. You probably want this:
def change
change_table :constants do |t|
t.string :home_address
end
end
or this:
def change
add_column :constants, :home_address, :string
end
Both forms should do the same thing: add a home_address string column to the constants table.
I'm also assuming that your change_table: constants is just a typo that should have been change_table :constants.
Further information may be found in the Migrations Guide.
You should do as below:
def change
add_column :constants, :home_address, :string
end

Adding a Model Reference to existing Rails model

I'd like to know the "proper" way to approach adding a relation between two existing classes in Rails 3.
Given existing models: Clown & Rabbit
I'd like to add a reference (belongs_to) from Rabbit to Clown. I start by trying to generate a migration:
rails g migration AddClownToRabbits clown:reference
which gives me a migration that looks like:
class AddClownToRabbits < ActiveRecord::Migration
def self.up
add_column :rabbits, :clown, :reference
end
def self.down
remove_column :rabbits, :clown
end
end
After rake db:migrate on this migration I examine SQLite3's development.db and see a new column: "clown" reference
I guess I was expecting a "clown_id" integer column and a migration that looked like:
class AddClownToRabbits < ActiveRecord::Migration
def self.up
add_column :rabbits, :clown_id
end
def self.down
remove_column :rabbits, :clown_id
end
end
I'm sure :reference is supposed to be equivalent to "t.references :clown" but I can't find the documentation (big surprise). API says add_column: Instantiates a new column for the table. The type parameter is normally one of the migrations native types, which is one of the following: :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean.
...with no reference to :reference.
If you are using edge rails (4.0) you can use:
rails generate migration AddAddressRefToContacts address:references
As you can see by the docs.
After you set belongs_to in Rabbit, and has_many in Clown, you can do a migration with:
add_column :rabbit, :clown_id, :integer
EDIT: See Paulo's answer below for a more updated answer (Rails 4+)
I'm not sure where you got this idea, but there is no (and never has been) such syntax to do what you want with add_column. To get the behavior you want, you'd have to do t.refences :clown, as you stated. In the background this will call: #base.add_column(#table_name, "#{col}_id", :integer, options).
See here.
EDIT:
I think I can see the source of your confusion. You saw the method call t.reference and assumed it was a datatype because calls such as t.integer and t.string exist, and those are datatypes. That's wrong. Reference isn't a datatype, it's just simply the name of a method, similar to t.rename is.

Alter existing model with a Redmine plugin

The Redmine plugin tutorials explain how to wrap core models but what I need is to add another column to the journals table.
I need a boolean field inserted in the journals model. Creating another model with a 'belongs_to :journal' relation seems like an overkill.
Can this be done with a plugin?
I should note that I am a rails newbie.
You just have to create the appropriate migration.
In your plugin's directory, create the file db/migrate/update_journal.rb with the following :
class UpdateJournal < ActiveRecord::Migration
def self.up
change_table :journal do |t|
t.column :my_bool, :boolean
end
end
def self.down
change_table :journal do |t|
t.remove :my_bool
end
end
end
Then you can execute the task rake db:migrate_plugins RAILS_ENV=production to update your database with the new field.
After executing the migration, your journal database will have the my_bool field that you'll be able to call like every other field.
I was able to extend the existing user model using the following code:
class UpdateUsers < ActiveRecord::Migration
def up
add_column :users, :your_new_column, :string, :default => ''
add_column :users, :your_other_new_column, :string, :default => ''
end
def down
remove_column :users, :your_new_column
remove_column :users, :your_other_new_column
end
end
Also I needed to name the migration file in way that it began with a number eg. myplugin/db/migrate/001_update_user.rb

Resources