Rails - Generating migration script from model - ruby-on-rails

I am learning rails, and I came across Migrations. It seems that everytime I want to edit a model, I would need to add a migration script, even though I am not yet in production.
Can you edit your model, add all your attributes you need to it, and before releasing it, have the migration script autogenerated?
Thanks!

If your using rails 3+ you might want to consider DataMapper instead of ActiveRecord. It lets you define the data model in the model instead of multiple migration files. As I understand DataMapper lets you generate migrations from changes.
This is a tried and trusted pattern often used in the wider ORM community.

I agree with the comments so far. The idea of migrations is to make it simple to fluidly adapt your data schema to fit your application as you want to add new fields. It's a simple and beautiful system.
So yes, you can (and should) use rails generate migration... as not only does this generate the proper code in many common cases, it also keeps track of which migrations have been run in different versions of the database. See http://guides.rubyonrails.org/migrations.html#creating-a-migration
A common workflow might be something like this:
create a new model, for example User with fields like first_name, last_name, user_name
this will create an associated migration, which you can run using bundle exec rake db:migrate -- your database schema will be updated
you decide you want additional information, such as birthdate, so run rails generate migration AddBirthdateToUser birthdate:date. For some simple operations like adding a column, index, etc., the full migration code will be generated; in other cases you'll need to write the migration. When done, run the migration.
If you find a problem in development, for example a field type should be float, not integer, or you forgot to add an index, you can roll back the migration (bundle exec rake db:rollback), fix the migration and re-run it.
run your tests (which will run the migrations), and when it all works for you locally, check in the files (including the migrations) and deploy to a QA or staging server, which has its own copy of the database.
run rake db:migrate on the staging server. If you're on a team and other developers have checked in migrations, their will run, too. Now your code and data schema are in sync.
repeat :-)
There's no harm whatsoever running migrations during a production deployment (I respectfully disagree with a comment above) -- you should embrace the idea that change, even changes like this (which can be incredibly difficult in other environments) are a normal part of everyday Rails life!

Related

Can I manually create migrations for production in Heroku, Ruby on Rails?

I have created an application with Ruby and Rails. The thing is that when I was develpoing it, I had some problems with the migrations, because I created them but with a wrong syntax. What happened is that I deleted some of the files because sold migrations that didn´t work had the same name than the new ones, but in the middle of that I accidentally deleted some of the migrations (obviously after running rails db:migrate) that the project uses actually. So for instance, i have the Service table, which is related to the Reservation table because Service has reservation_id, but i don´t have the migration file that says AddReservationIdToService.
So now I want to use Heroku for production. the thing is that O have to change to postgresql because Heroku doesn't support sqlite. So i have to run the de:migrate again to create the tables and relationships in the new DB, but I need the files that I explained that I deleted. The question is:
Can I create the migrations manually, so when i run db:migrate for postgres the full structure of the database is created without lacking relations?
You don't really need the migrations to recreate the existing DB -- in fact it's not a good idea to try for a couple of reasons (including the missing migration file problem you encountered). You can simply run:
bin/rails db:schema:load
to populate a new database from the existing schema. If for some reason you haven't got a db/schema.rb checked under version control you can run:
bin/rails db:schema:dump
against the sqlite version to re-create a fresh schema file from the database.
You can also keep your migrations list tidy by occasionally zapping really old migrations, since all the cumulative changes are captured in the schema file.
Yes, you might create another couple of migration files.
Certify you have now the tables you wish locally with your sqlite. Draw these table in a piece of paper (or where it be the best fr you), then check this official API documentation of Rails.
Delete all migrations made before and create another according to the tables you drew.
The workflow is gonna be like:
1) "I need to create a table called Reservation, where is it shown on the documentation?"
2) "I need a table called Service, where is it shown on the documentation?
3) "I need to add a column with a foreign key to service named reservaton_id, how does this documentation says it?
For all this steps above, create the correspondent migration file as you normally have done.
The main difference here is not to run the migration locally. Instead, push your new version app to your heroku remote branch and there you run the migration, like:
heroku run rails db:migrate
Remember to not run this same migration locally because you already have these tables locally.
The last two advise is:
1) If your migration doesn't go as you expect, don't delete the migration file. Instead, run rails db:rollback and try again.
2) Keep tracking your migration files on the same branch of your version control.

how devise db:migrate works on production

I'm doing my first project on RoR I decided to use Devise gem for the authentication everything is working fine in Localhost, right now i'm able to sing_up, log_in, reset pass and some other default features that comes with devise.
What I don't understand is how everything gets setup once i push the project into production environment. Let me explain better.
-I see that devise generates db/schema.rb file based on the user.rb model that devise auto generate, these schema i assume needs to be created in the production db.
I have to push the project into git repo but..
1) How the code gets executed once is published
2) How the user table gets created
3) where do I need to specify the db connection (db, user, pass)
In general how is the process to migrate into production env?
I appreciate your help.
In rails db schema is controlled using migrations. Migrations are usually very small files living in db/migrations folder. Each of them defines a single subclass of ActiveRecord::Migration class.
Each migration is supposed to go both-ways, so it has to implement both up and down method (for rollback). In majority of the changes those are obvious (like when up action creates a table, down action should destroy it). For migrations like this you can use change function - rails will create down function automatically (assuming that all actions are reversible - some of the methods even accept extra arguments to make it possible like remove_column :table, :field, :integer. :integer bit might seem redundant here, however the action is irreversible without that information).
You run your migrations using db:migrate rake task. Effectively it will run all the migration which has not been run against current database. When it is done, it will dump your current database into db/schema.rb file. This file is used only as a snapshot of your current database schema, which is of great help if you need to get back to previous version of your application (which is why schema.rb should always be checked into your source control). It is also possible to recreate your database directly from that file rake db:schema:load, however it will cost you all your data.
If you check your database (any environment), you will notice there is one extra table called schema_migrations which does not appear in your schema file. This table contains all the migrations that has been executed against this database. This way rails will not retry migrations that already has been run.
There are many advantages of using migration system:
It is easy for every developer to bring their local database up to the latest schema.
It is easy to rollback your latest changes if deployment failed. (Not always the case)
So how to use it in production? You simply has to point the rake task to the right database:
RAILS_ENV=production rake db:migrate
You can find your database configuration in config/database.yml file. Note that this is the best practice not to store that file in source control and in case of production it is not advisable to store your database password there. Normally you will need to set it through environment variable.
It is important to remember couple of things about migrations:
Never, ever change the existing migration if there is a chance that someone has already run it. Especially, do not modify migrations that has run in production unless you are 100% sure what you are doing (for example fixing rollback). Since database remembers which migrations has run, it might lead to getting your local db out of sync with production db and hence lead to bugs.
Never ever change database schema manually - every change has to be made through migrations.
Well depending on where you're hosting your code you should still do a rake db:migrate to make the changes to the production DB.
If deployed on Heroku, run the following in the console:
heroku run rake db:migrate
Hope that answers your question?

Rails - autogenerate migrations from database changes

Is it possible to use Rails to auto-generate a migration based on changes that took place in the database outside of Rails since the last migration?
I know that running db:migrate will change the schema.rb to match what's in the database.... (at least if don't make any migrations but I do change the database in some way manually). What I'm wondering is, if there is a way as part of that same mechanism or process to have it create a migration out of those changes.
Many thanks!
I don't think this is something Rails would do or is trying to solve. Rails wants you to explicitly manage those changes through migrations so that your database is under control (and source control).
See if this gem is what you want. - https://github.com/pjhyett/auto_migrations
It might not work with Rails 3. I think since you edit your DB directly you'd have to run rake db:schema:dump to update the schema.rb file.
If you want to capture the deltas between the different db change points then see this SO answer:
How to generate Rails Migration class automatically from MYSQL database instance? , just repeat his steps along the way.

Aggregate migrations in Rails

I have several dozens Rails DB migrations which were written over a year. Is there a way to aggregate them to one migration so that I will just see a full DDL statement for the database as it exists now? I just need the current snaphot without all the history of how we got to it.
It is possible, but probably not a good idea to aggregate the migrations!
Maybe ask:
Why do you want to do this?
How often do you really need to migrate all the way to VERSION=0 and then back up again?
Is something really broken? (if not, then don't fix it)
I've had the same problem once.. I ended up just re-ordering my migrations, because changes in the schema caused it to not correctly migrate up/down anymore. I would be hesitant to do that again.
If you have migrations which just add fields or indexes, then maybe you can combine them with the main migration for the model -- but beware that you can't reproduce old situations anymore, e.g. older DB-dumps may not be compatible with what migration number they should be compatible with -- that is probably the biggest argument against aggregating...
Technically, you can dump the schema and then load it directly - that is one way:
rake db:schema:dump
then create a single new migration with the contents of the schema dump file db/schema.rb
Here are some similar questions:
Rebase Rails migrations in a long running project
Deleting/"Rebasing" rails migrations
Way to "flatten" Rails migrations?
Should I flatten Rails migrations?
P.S.: I found it useful to stick with the old migration numbering scheme, where the migrations do not use timestamps - for me this works better (is easier to see in which order they are).
e.g. in your config/application.rb file:
config.active_record.timestamped_migrations = false
You should never be using all the migrations to get a database up and running. The current schema.rb is always what the DB looks like 'presently'.
It's good practice to periodically just truncate your migrations if you have a ton of them in there. We finally did that with one of our larger applications, removing a good 50 migrations from the folder because the only thing that matters is schema.rb. Migrations are just that, a way to migrate and make changes to an existing state of the database. They should only ever have to be run once.
You can simply load the current schema into the DB.
rake db:schema:load RAILS_ENV=[production, test, etc.]
This will take the schema.rb file's version of the schema, and load it into the DB without running individual migrations.
NOTE: if you have migrations that put data into the DB (e.g. default values, for example), that data will not be added to the DB.
If you need to load default values into your DB, that might be better done via a custom rake task, independent of migrations.

Is it a good idea to purge old Rails migration files?

I have been running a big Rails application for over 2 years and, day by day, my ActiveRecord migration folder has been growing up to over 150 files.
There are very old models, no longer available in the application, still referenced in the migrations. I was thinking to remove them.
What do you think? Do you usually purge old migrations from your codebase?
The Rails 4 Way page 177:
Sebastian says...
A little-known fact is that you can remove old migration files (while
still keeping newer ones) to keep the db/migrate folder to a
manageable size. You can move the older migrations to a
db/archived_migrations folder or something like that. Once you do trim
the size of your migrations folder, use the rake db:reset task to
(re-)create your database from db/schema.rb and load the seeds into
your current environment.
Once I hit a major site release, I'll roll the migrations into one and start fresh. I feel dirty once the migration version numbers get up around 75.
I occasionally purge all migrations, which have already been applied in production and I see at least 2 reasons for this:
More manageable folder: it is easier to spot a new migration.
Cleaner text search results: global text search within a project does not lead to tons of useless matches because of some 3-year-old migration when someone added or removed some column which anyway does not exist anymore.
They are relatively small, so I would choose to keep them, just for the record.
You should write your migrations without referencing models, or other parts of application, because they'll come back to you haunting ;)
Check out these guidelines:
http://guides.rubyonrails.org/migrations.html#using-models-in-your-migrations
Personally I like to keep things tidy in the migrations files. I think once you have pushed all your changes into prod you should really look at archiving the migrations. The only difficulty I have faced with this is that when Travis runs it runs a db:migrate, so these are the steps I have used:
Move historic migrations from /db/migrate/ to /db/archive/release-x.y/
Create a new migration file manually using the version number from the last run migration in the /db/archive/release-x.y directory and change the description to something like from_previous_version. Using the old version number means that it won't run on your prod machine and mess up.
Copy the schema.rb contents from inside the ActiveRecord::Schema.define(version: 20141010044951) do section and paste into the change method of your from_previous_version changelog
Check all that in and Robert should be your parent's brother.
The only other consideration would be if your migrations create any data (my test scenarios contain all their own data so I don't have this issue)
Why? Unless there is some kind of problem with disk space, I don't see a good reason for deleting them. I guess if you are absolutely certain that you are never going to roll back anything ever again, than you can. However, it seems like saving a few KB of disk space to do this wouldn't be worth it. Also, if you just want to delete the migrations that refer to old models, you have to look through them all by hand to make sure you don't delete anything that is still used in your app. Lots of effort for little gain, to me.
See http://edgeguides.rubyonrails.org/active_record_migrations.html#schema-dumping-and-you
Migrations are not a representation of the database: either structure.sql or schema.rb is. Migrations are also not a good place for setting/initializing data. db/seeds or a rake task are better for that kind of task.
So what are migrations? In my opinion they are instructions for how to change the database schema - either forwards or backwards (via a rollback). Unless there is a problem, they should be run only in the following cases:
On my local development machine as a way to test the migration itself and write the schema/structure file.
On colleague developer machines as a way to change the schema without dropping the database.
On production machines as a way to change the schema without dropping the database.
Once run they should be irrelevant. Of course mistakes happen, so you definitely want to keep migrations around for a few months in case you need to rollback.
CI environments do not ever need to run migrations. It slows down your CI environment and is error prone (just like the Rails guide says). Since your test environments only have ephemeral data, you should instead be using rake db:setup, which will load from the schema.rb/structure.sql and completely ignore your migration files.
If you're using source control, there is no benefit in keeping old migrations around; they are part of the source history. It might make sense to put them in an archive folder if that's your cup of coffee.
With that all being said, I strongly think it makes sense to purge old migrations, for the following reasons:
They could contain code that is so old it will no longer run (like if you removed a model). This creates a trap for other developers who want to run rake db:migrate.
They will slow down grep-like tasks and are irrelevant past a certain age.
Why are they irrelevant? Once more for two reasons: the history is stored in your source control and the actual database structure is stored in structure.sql/schema.rb. My rule of thumb is that migrations older than about 12 months are completely irrelevant. I delete them. If there were some reason why I wanted to rollback a migration older than that I'm confident that the database has changed enough in that time to warrant writing a new migration to perform that task.
So how do you get rid of the migrations? These are the steps I follow:
Delete the migration files
Write a rake task to delete their corresponding rows in the schema_migrations table of your database.
Run rake db:migrate to regenerate structure.sql/schema.rb.
Validate that the only thing changed in structure.sql/schema.rb is removed lines corresponding to each of the migrations you deleted.
Deploy, then run the rake task from step 2 on production.
Make sure other developers run the rake task from step 2 on their machines.
The second item is necessary to keep schema/structure accurate, which, again, is the only thing that actually matters here.
It's fine to remove old migrations once you're comfortable they won't be needed. The purpose of migrations is to have a tool for making and rolling back database changes. Once the changes have been made and in production for a couple of months, odds are you're unlikely to need them again. I find that after a while they're just cruft that clutters up your repo, searches, and file navigation.
Some people will run the migrations from scratch to reload their dev database, but that's not really what they're intended for. You can use rake db:schema:load to load the latest schema, and rake db:seed to populate it with seed data. rake db:reset does both for you. If you've got database extensions that can't be dumped to schema.rb then you can use the sql schema format for ActiveRecord and run rake db:structure:load instead.
Yes. I guess if you have completely removed any model and related table also from database, then it is worth to put it in migration. If model reference in migration does not depend on any other thing, then you can delete it. Although that migration is never going to run again as it has already run and even if you don't delete it from existing migration, then whenever you will migrate database fresh, it cause a problem.
So better it to remove that reference from migration. And refactore/minimize migrations to one or two file before big release to live database.
I agree, no value in 100+ migrations, the history is a mess, there is no easy way of tracking history on a single table and it adds clutter to your file finding. Simply Muda IMO :)
Here's a 3-step guide to squash all migrations into identical schema as production:
Step1: schema from production
# launch rails console in production
stream = StringIO.new
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream); nil
stream.rewind
puts stream.read
This is copy-pasteable to migrations, minus the obvious header
Step 2: making the migrations without it being run in production
This is important. Use the last migration and change it's name and content. ActiveRecord stors the datetime number in it's schema_migrations table so it knows what it has run and not. Reuse the last and it'll think it has already run.
Example: rename 20161202212203_this_is_the_last_migration -> 20161202212203_schema_of_20161203.rb
And put the schema there.
Step 3: verify and troubleshoot
Locally, rake db:drop, rake db:create, rake db:migrate
Verify that schema is identical. One issue we encountered was datetime "now()" in schema, here's the best solution I could find for that: https://stackoverflow.com/a/40840867/252799

Resources