I am just curious,
Suppose I create this migration:
def change
create_table :pages do |t|
t.string :title
t.text :content
t.timestamps
end
end
and then I run the migration.
Now after a couple of hours I remember that I should have added a slug column too.
Now (supposing I haven't created any other migrations after this one), should I rollback and add the new field here (in this migration), or should I create a new migration and add the filed there?
If you haven't pushed it out to production, I'd say just roll back and add it into the existing migration. If you have deployed it to production, then I'd make another migration.
This is, however, mostly a matter of preference, if there's no data that will be lost as a result. I just like to get into the habit of doing things the safer way in production.
Related
I'm retroactively writing ActiveRecord migrations inside a quirky, legacy Rails app that did not follow convention so well.
The models already exist, but were not made with a generator so the tables and everything in the past were set up by hand.
I'm changing that, and I made a migration to start (I'm also switching db behind the app from MS SQL Server to Postgres).
The migration looks like this:
class CreateSuite < ActiveRecord::Migration
def change
create_table :suites do |t|
t.string :name
t.string :owner
t.string :resource_dependencies
t.datetime :requested_time
t.datetime :finished_time
t.string :status
...
end
end
end
The application, which we'll say is called foo is set as an active_record.table_name_prefix in the application.rb:
config.active_record.table_name_prefix = 'foo_'
After the migration, I want a table that is named 'suites' but instead I get 'foo_suites'. I do not want to change the prefix in application.rb since it's there for a reason (the application needs another table called 'foo_suites' but I'm going to tackle that with another migration file.)
How do I make this ActiveRecord::Migration create 'suites' instead of 'foo_suites'?
1) I've created an entire Rails JSON API as my first project. It's fairly sizeable, and I put off database level constraints at the time. It's time to do it now.
I have 30+ or so migrations. For example
class CreateItems < ActiveRecord::Migration
def change
create_table :items do |t|
t.string :name
t.text :description
t.timestamps
end
end
end
When I go in and edit
t.string :name
to be
t.string :name, null: false
and run a bundle exec db:reset db:migrate it doesn't work. What do I need to do to add db level constraints to my migrations files, and re migrate them?
2) Also, my 30+ migrations all have a "def change" and that's it. I believe somewhere a long time ago I saw something like def up, def down or something like that. It looks like it was used for rollbacks. Do I need to have "def down" for all of my migrations? Is that how I solve #1? Put "def down" in all of my 30 migrations, rollback the entire thing, then put in my db level constraints, and migrate the whole thing again?
You have two options: you can write new migrations to make changes to the existing columns. In console:
rails g migration add_null_to_name
In the migration file:
class ChangeNumbericFieldInMyTable < ActiveRecord::Migration
def self.up
change_column :my_table, :name, :string, :null => false
end
def self.down
change_column :my_table, :name, :string
end
Rinse and repeat for all the changes you need to make. Note that you can define them in the same migration file, but then if you had to rollback in the future it would roll back all of them.
Your second option is to roll everything back (or just start over), fix your migration methods and remigrate. This might be a cleaner approach, but that's not necessarily a good thing: it would also delete the existing data, if you have any.
I know I said you have two options, but you actually also have a third option, which is to to rely on model validations instead. These are more appropriate in most situations and can make testing much easier. Database validations are more reserved for complex scenarios where the database is used by other applications in addition to yours, and a few other edge cases.
As for your second question, every migration is a two-way street. When you migrate something, you should also be able to roll it back. In a lot of cases Rails can guess the reverse operation. For example, if you add_column in a migration, the opposite of that is obviously remove_column so you don't need to define it explicitly. But certain operations, such as change_column in the example above, don't have an obvious reverse. Rails can understand that something changed, but it can't tell what it changed from. So in those situations you need to explicitly define the down operation. In the case above, it's changing the column back to a state where it doesn't have a validation.
Ok so you've built your model 'rails generate model test (inputs)' you've rake db:migrate checked your schema file all looks good and working and writing to sqlite. But then you realise you need another attribute in the DB. One way is to rake db:rollback, make change in the migrate file, rake db:migrate again and hey presto all looking good. However doing rake db:rollback has lost all my data already stored in the sqlite. So I'm guessing this is not the correct way to do this? What is the best way?
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.text :title
t.text :requester
t.text :requester_email
t.text :customer
t.text :contact
t.text :customer_email
t.text :customer_phone
t.string :type_of_change
You can generate a new migration with rails g migration <migration_name> (eg. rails g migration add_foo_to_blah) and then inside that new migration, make the changes you need.
eg. you can add new columns, rename columns, etc.
For more information: http://guides.rubyonrails.org/migrations.html#creating-a-standalone-migration
In fact db:rollback is best way. Of course when you do db:rollback it will wipe away your existing data,but you need to do db:migrate to get the data appear again in the tables.
If you don't want to do rake db:rollback,then create another migration file which will do the job for you.
Edit
You need to perform the below command to get that column added in your posts table.
rails g migration AddImplementerToPosts implementer:text
and do rake db:migrate to make sure that column added in your table.
Yes, db:rollback takes back the last migration, so if in last migration you added some columns, db:rollback will remove them (and if you change the migration and run it again, these fields will be added, but data is already lost).
The proper way to add more fields is to generate new migration and add these fields there.
I'm getting an error:
SQLite3::SQLException: no such column: ideas.list_id:
SELECT "ideas".* FROM "ideas"
WHERE "ideas"."list_id" = 2
But I added
t.integer :list_id
to my db migration file:
class CreateIdeas < ActiveRecord::Migration
def change
create_table :ideas do |t|
t.string :name
t.text :description
t.string :picture
t.timestamps
end
add_foreign_key :ideas, :lists
end
end
which gave me this:
class CreateIdeas < ActiveRecord::Migration
def change
create_table :ideas do |t|
t.string :name
t.text :description
t.string :picture
t.integer :list_id
t.timestamps
end
add_foreign_key :ideas, :lists
end
end
and then I typed
rake db:migrate
Any idea why I would be getting an error saying there's no column? I'm still new to RoRs. Do I have to add a column some other way?
Thanks
As Speransky suggested, you should never modify old migration files. Rather you should create a new migration that adds the desired column. For instance, in this case you would run the following command in your app to create the new migration:
rails generate migration AddListIdColumnToIdeas list_id:integer
And Rails would generate the migration file automatically and the only thing left to do is run rake db:migrate.
If you insist on modifying the old migration file, you can add the column as you did and run the following:
rake db:drop
rake db:create
rake db:migrate
Which will destroy your current database, create a new one and run all the migrations (which will include your new column).
If you want to add a new column to an exist database, you should use rails generate migration. So you can try rails generate migration add_list_id_to_ideas list_id:integer and then use rake db:migrate to commit this change.
You should not add new rows to old migrations. Migration is a step of building database. And number of last executed migration is stored in schema, and it will not be run or redone if you use will use rake db:migrate. If you run the migration with creating the table before, then you should create new migration where you may use add_column method.
migration file name has the datetime encoded in its name so rails run this migration one and do not run it again unless you do a rollback
and here come the magic of migration to build you db with small steps so no need to update a migration after run rake db:migrate , you should make a new migration to do the change you want to your db schema
and remember to
remove the added line form the old migration file as it might raise errors if you decided to rollback this migration
Rails 4.0 easy way to add single or multiple column
https://gist.github.com/pyk/8569812
You can also do this ..
rails g migration add_column_to_users list_id:string
then rake db:migrate
also add :list_id attribute in your user controller ;
for more detail check out http://guides.rubyonrails.org/active_record_migrations.html
If you already have files in your migrate folder, you could just add column you want there(just type the code), delete development.sqlite or whatever represents your db file, and run rake db:migrate.
It will then create a new sqlite file with new column in table, and you can check it in schema.rb
So, basically, everything you did seems good, except you didn't delete your database file.
Doing this seems the easiest for me, all though you will lose all the files in your database. If you're just testing and developing Rails app, this works.
Can anyone comment if there is something wrong with this approach, besides what i wrote?
Edit: I actually found an answer about that here
Editing Existing Rails Migrations is a good idea?
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