adding a new column in a rails database migration - ruby-on-rails

So I've been going through a lot of rails tutorial, and I get that the default for adding a new column to a database is , for example,
rails generate migration add_reset_to_users reset_digest:string reset_sent_at:datetime
The above will add a reset_digest in the form of a string and reset_sent_at in the form of a date to the migration add_reset_to_users
My questions is what if I am clumsy one night at 4 AM and only call the following
rails generate migration add_reset_to_users reset_digest:string
I completely forgot about reset_sent_at but want to implement it the next morning. I made the mistake of adding the link directly to the db file, which was a huge mistake.
In this case what should I do? Do I simply call a new migration such as
rails generate migration add_reset_sent_to_users reset_sent_at:datetime
or is there an even better way?

first, if you have not run your migration, you can directly open the migration file, and add your column to the file, as
def change
add columns :table_name :column_name :column_type
end
In your case, you will modify the file as,
def change
add columns :users :reset_digest :string
add columns :users :reset_sent_at :datetime
end
and then run
rake db:migrate
if you have already ran your migration, and you have not run any other migration after that, you can undo it, by
rake db:rollback STEP=1
and then edit the migration file, and run your migration

Rule of thumb for migrations in Rails is that you always create a new migration file unless you have not already shared your code with others, i.e. pushed to remote repository, otherwise you can just change the old migration after running $ rake db:rollback and everything will be fine, and nobody will know about it, and won't affect other developers work(since it's still on your local repository).
So, I'd encourage you to create a new migration if you have already committed and pushed the code onto remote repository, and changing the old migration file again will hurt other developers productivity. In case of any confusion, always create a new migration:
rails generate migration add_reset_sent_to_users reset_sent_at:datetime

I think it depends what state your rails app is in.
If you were working on a production app then editing migrations is not advisable due to data loss and changes should be made with a new migration.
If your working locally in development then I would edit the migration directly and add the missing column and rerun your migration.
Don't be worried about editing you migrations, just remember to rake db:rollback the migration you are editing before making changes or you will encounter errors.
This is where changing your migration from:
def change
add_column('users', 'reset_digest', :string)
add_column('users', 'reset_sent_at', :datetime) # Would have to perform rollback before adding this line
end
to:
def up
add_column('users', 'reset_digest', :string)
add_column('users', 'reset_sent_at', :datetime) # Added after migration
**rake db:migrate
end
def down
remove_column('users', 'reset_digest', :string)
remove_column('users', 'reset_sent_at', :datetime) # Add this after rollback
**rake db:rollback
end
Allows you to make changes to your migrations before you rake db:rollback
This requires a bit more code but I find it easier when I'm building a new app and things are changing frequently.

Related

Add and Change the default value for table column with migration

I currently have a migration and I simply want to add a couple more values to this migration such as to_limit and from_limit along with their default values (5&10). How should i do this?
class AddUserToCompany < ActiveRecord::Migration
def change
add_column :companies, :user, :jsonb, default: {"to_dist"=>50, "from_dist"=>70, "trs_one"=>100, "trs_two"=>120}
end
end
I added some changes which also requires "to_limit"=>5 and "from_limit"=>10. Please suggest on how to add a new migrate file and add the changes.
Rails version - 4.2.11
This is my first time working with a rails application. So i am really confused on how to proceed with this.
1.If this changes are in progress (not already used by other) then you can rollback this migration by command
rollback last migration
rake db:rollback STEP=1
In order to rollback ONLY ONE specific migration
rake db:migrate:down VERSION=20220905201547
and after applying changes, run rake db:migrate
If migration was created earlier then you can create new migration to apply your changes
generate new migration by command
rails generate migration nameOfMigration
I hope this helps

manually adding an integer field to rails scaffold

I'm new to rails...I made a scaffold "Reviews" but want to add another field "ratings", as an integer. I did the following:
added t.integer :ratings in the migration file..ran rake db:migrate
in spec folder: added it in views/app/ edit, index, new, show
in app/views/app added it in the json files
in app/controllers/app added it in the review_params function
still however whenever I try to reference (by showing a Review) it I get
undefined method `ratings' for #
There must be something else I need to add somewhere to have it be part of my Reviews scaffold. I've been trying to figure it out for 5 hours but still have not. When I try to remake a scaffold and run rake db:migrate I get an error saying that the databases already exist so I would like to just manually add it to my existing one if possible, I just can't seem to figure out how even though I've already done it once for a string.
Any help is appreciated thank you.
To add an integer field to a model you can do something like this.
rails generate migration AddRatingToReviews rating:integer
This should handle everything for you by generating a new migration file like so.
class AddRatingsToReviews < ActiveRecord::Migration
def change
add_column :reviews, :rating
end
end
Then you can run rake db:migrate to add the column to your review model.
NOTE: Before doing all of this, please delete all your manual changes. If necessary use rake db:rollback which will rollback your most recent rake db:migrate.
Recommendation
If you are new to rails and don't understand MVC, I suggest not using scaffolding because you'll have a tough time knowing what it is doing. Go through this awesome tutorial by Michael Hartl to really learn rails quickly. http://ruby.railstutorial.org/
*Awesome gem *
Use the annotate gem to display the attributes contained within your model directly in your name_of_model.rb files.
https://github.com/ctran/annotate_models
rails generate migration AddRatingsToReviews ratings:integer
Then
rake db:migrate
If you have not done any major changes in your generated scaffold.
Simplest way to get the ratings across views would be as below:
Rollback the changes that you have migrated
rake db:rollback VERSION=version_number
Where replace version_number with the version_number prefixed on your migration file.
For eg: If your migration filename is 20140314190622_create_reviews.rb then command should be
rake db:rollback VERSION=20140314190622
Destroy the scaffold of Review
rails d scaffold Review
After this generate the scaffold again with the integer field
rails g scaffold Review ratings:integer .... ## Add other field in place of ....

How does the migration work in Rails?

This is a newbie question. Is the migration (rake db:migrate) feature something that one would using during development or it's strictly a production tool for bringing the database up to date?
Ideally you want to use migrations only during development, and then load schema and seed the database in production. In reality, they'll allow you to make some changes and then deploy to production without any harm done.
Migrations allow you to work in iterations even on your database. You don't have to worry about forgetting to add something. When you start, just create the table as you think it's right, and you can fix it with another migration later. That's basically the idea. It takes away the one db script rules them all kind of thing.
A little example, if you have a User model with username and password and you need to add an email field, simply do this
rails generate migration AddEmailToUser # this is a convention, but you can name it however you want
class AddEmailToUser < ActiveRecord::Migration
def change
add_column :users, :email, :string
end
end
the change method will work both ways, when you apply the migration, and also when you need to revert it. It's kind of a neat Rails 3.1 magic.
The old version of migrations would look like this
class AddEmailToUser < ActiveRecord::Migration
def up
add_column :users, :email, :string
end
def down
remove_column :users, :email
end
end
Once you've added the migration, just run rake db:migrate and everything should work just fine. A big advantage of migrations is that if you manually mess up your database, you can easily just do
rake db:drop
rake db:create
rake db:migrate
or
rake db:migrate:reset # this might not work if you messed up your migrations
And you have the correct version of the database created
Migrations keep track of changes in your database schema. All changes with it (renaming column, changing tables, adding indexes etc.) should be done via migration. Thanks to that is really easy to deploy changes across multiple production servers.

Rails: how to rollback a botched migration

I'm an idiot...screwed up a migration in Rails:
thinking migrations would work like model generators (using references:modelname) I did the following:
$ rails g migration add_event_to_photos references:event
which created the migration
class AddEventToPhotos < ActiveRecord::Migration
def change
add_column :photos, :references, :event
end
end
And now my development database (SQLite3) has a references column of type event in the photos table.
And my schema.rb has a line in the middle saying:
# Could not dump table "photos" because of following StandardError
# Unknown type 'event' for column 'references'
rake db:rollback is powerless against this:
$ rake db:rollback
== AddEventToPhotos: reverting ===============================================
-- remove_column("photos", :references)
rake aborted!
An error has occurred, this and all later migrations canceled:
undefined method `to_sym' for nil:NilClass
So, how to roll back and maintain my development data in the database? I'd even be happy trashing the photos table if that's my only choice..but don't want to have to rebuild the whole thing. What to do?
btw- for anyone reading this about to make same stupid mistake...don't! Use the correct migration generator:
$ rails g migration add_event_to_photos event_id:integer
The easiest way I found to do this was to recreate the table in the schema.rb file in /db/. Afterwards I ran a rake db:reset (if it says you have pending migrations, just delete them and try again).
This took care of the problem.
Go into the database by ./script/rails dbconsole. Then type these commands:
.output dump.sql
.dump
In the file dump.sql you will have the SQL commands used to recreate and populate your database. Just edit it with your favourite editor (like vim ;-) removing or fixing the column type. You may also remove the invalid migration identifier from the schema_migrations table. Drop your database (I suggest just rename the db/development.sqlite file), create new database and read the dump file into it (using command .read dump.sql).
Now you just need to fix and run your migrations.
add an empty down method and run rake db:rollback
edit ahh that's the new migration syntax, you can replace the body with simply:
def self.down; end
which is the old syntax, or perhaps delete the body altogether (haven't tried this) and then run rake db:rollback
Just an idea, I know it's not SQLite specific you can revert to an older version schema perhaps, load it up. And try again from there? You can revert (checkout) specific files in GIT. And then do def self.down; end, as was suggested by another poster.
The problem arises because while SQLite will create the schema with whatever type you give it (in this case event it can't dump that type back to ActiveRecord.
You need to edit the sqlite_master file and change create table string (sql) to be the right thing.
You probably want to back up your table first since messing up that string will wreck your table if you do it wrong.
Here is a related rails issue

How do I add a new attribute to a migration in Rails 3?

Here's my story: I had something like this set up a while ago.
create_table(:users) do |t|
t.database_authenticatable :null => false
t.recoverable
t.rememberable
t.trackable
t.integer :total_pageviews
and "total_pageviews" worked fine. I could set it to zero, increment it, multiply it, call it, whatever. but let's say i want to add:
t.integer :total_votes
What do I have to do to configure total_votes as an attribute? Whenever I try to use "total_votes", I get undefined method error. I'm thinking that the answer is something like rake db:migrate or similar.
You should not ever alter existing migrations. If you have checked them in to your version control system and somebody else runs them and then you add a new field to them and commit that change, they will not know to get that change. Then you must tell them and it's a pain in the butt to fix.
If you've not committed it yet then rollback the migration (rake db:rollback), make the necessary modification and re-run it again (rake db:migrate).
But in the Real World, you would create a new migration which adds this field using rails g migration add_total_votes_to_users total_votes:integer.
Rails will interpret this migration name and know what to do with it, generating a migration that contains this line:
add_column :users, :total_votes, :integer
Then when you run this migration it will add this total_votes field to the users table which will make an attribute of the same name for all User objects.
Are you adding this after the migration has been run? So, the users table exists and you want to add a column to it? If so, you need go create a migration like
rails generate migration add_total_votes_to_users
Open it up (just to verify that the table and column are correct). Then run the migration.
If you app is in production, then you should create a new migration. If you app is in pre-production stage, and you have a small development team with good communication, then I recommend you to go ahead and modify the migration and then do
rake db:drop
rake db:create
rake db:migrate
then you can commit the modified migration file and let the rest of you team know that that you have changed a migration file.

Resources