Migrations code not generated in schema.rb? - ruby-on-rails

I had to put execute into one table migration. It looks like this:
class CreateFoos < ActiveRecord::Migration
def up
create_table :items do |t|
t.integer :bar
end
execute("GRANT SELECT ON items TO otheruser;")
end
def down
drop_table :items
end
end
This works well, but db/schema.rb file, which should be authority for database creation, is missing that line with execute command.
Is there something I'm missing or this is default behavior when schema.rb is generated?
I can bypass this issue by simply ignoring schema.rb and generating tables with rake db:migrate when deploying, but I saw recommendations to avoid doing so.
Any ideas?

Active Record's schema dumper can't handle database specific items like foreign keys, constraints, grant statements, etc. Change your database format to sql instead of of ruby. In your application.rb file:
config.active_record.schema_format = :sql
This will use a database specific tool to dump the schema to db/structure.sql. For example, if you are using PostgreSQL it will use pg_dump to dump the schema. The drawback to using the sql format is that the dump is no longer database agnostic. If you were to migrate from PostgreSQL to MySQL you would not be able to use the generated structure file to create a new database.
You can also generate a sql dump with the rake command:
rake db:structure:dump

Related

Issue with Rails migration for secondary database when unit test is running

I have a Rails 4 application that uses two MySQL databases (primary, secondary). Both databases are configured for development, production and test environments in the database.yml file:
development:
...
database: primary
...
production:
...
database: primary
...
test:
...
database: primary
...
secondary_development:
...
database: secondary
...
secondary_production:
...
database: secondary
...
secondary_test:
...
database: secondary
...
For now I only have one model that is stored in the secondary db. Below is the migration code that creates a table for this model:
class CreateTags < ActiveRecord::Migration
ActiveRecord::Base.establish_connection "secondary_#{Rails.env}"
def change
create_table :tags do |t|
t.string :name
t.integer :account_id
t.timestamps
end
end
end
When I run rake db:migrate, the table is correctly created in the secondary database. But when I run rake db:migrate a second time, it shows me an error table already exists, which I think related to the fact that rake task adds migration version to versions table of primary database. I am ignoring this for now.
But, when I run some unit test using rake test TEST=test/path_to_test_file.rb, it shows me an error saying Tags table does not exist in the secondary DB. I've checked the logs and found that Tags table is created, BUT in PRIMARY database which is wrong.
So, in short, how to change the migration code to make sure that Tags table will always be created in the secondary DB?
I have tried:
https://stackoverflow.com/a/11020572
https://stackoverflow.com/a/9652660
But it is not working for me :(
UPDATE 1:
Based on #User089247 suggestion, I have tried to run RAILS_ENV=test rake db:create and RAILS_ENV=test rake db:migrate. It says that my primary database is already created which is true, but it says nothing about my secondary databse because secondary DB has separate configuraion secondary_test. Based on my understanding, it should be possible to create custom rake task (or override existing one) but then this taks should be used by rake test. Is it possible ? Or am I missing somethings ?
What you want to do here is specify the usage of the secondary database on the model, not just in the migration:
class Tag < ActiveRecord::Base
establish_connection "secondary_#{Rails.env}"
end
This blog post has some helpful additional info if you are going to be using the secondary database for multiple models: http://pragdave.me/blog/2006/01/03/sharing-external-activerecord-connections/

Alter Schema in Rails 2

I need to add some columns to a table in my schema. Can someone tell me the best way to do this?
The following seems incomplete or wrong since the schema.rb file did not update to include the new column and all of the corresponding view files (edit,index,new,show) did not update to include the new column. Not to mention the bloat of all of those migration classes that get generated. Thanks
ruby script/generate migration RecordLabelToAlbums record_label:string
exists db/migrate
create db/migrate/20121130125859_record_label_to_albums.rb
Creates this:
class RecordLabelToAlbums < ActiveRecord::Migration
def self.up
end
def self.down
end
end
I then added this:
class RecordLabelToAlbums < ActiveRecord::Migration
def self.up
add_column :albums, :record_label, :text
end
def self.down
remove_column :albums, :record_label
end
end
The I ran:
rake db:migrate
Got This:
Mysql::Error: Table 'albums' already exists: CREATE TABLE albums (id int(11) DEFAULT NULL auto_increment PRIMARY KEY, created_at datetime, updated_at datetime)
The code you added is correct.
The error suggests that for some reason your system appears to think it has not yet run the original migration that created the albums table. The state of migrations (in Rails 2) is specified in a table in the database called schema_migrations -- if this gets confused then it will try to re-run migrations. I am not sure what might cause it to get confused, but I do recall this happened a couple times back in 2008 when I was using Rails 2.x.
The table is simple -- you can see what's in it from a SQL prompt -- just the names of migrations it thinks it has run, I think.
If you don't mind losing some data, you can try rake db:rollback or even rake db:reset to get back to the beginning. rake db:rollback STEP=2 will rollback the last 2 migrations.
If you need the data, correct the contents of the table by adding one or more new records referencing the migrations in app/db/migrations that may have been missed. The order is important, I think (the format changed a little in Rails 3, I don't recall how).
Any time you want to add or change the database schema, use rails to generate a migration, and then run rake db:migrate once it's ready to go.
And just asking: is there any way you can move to Rails 3. It's been out for years now, and Rails 4 is coming soon. You'll find yourself in a backwater of incompatibilities, deprecations, security and performance issues and so on if you don't take the hit and upgrade.

Multiple database connections: schema_migrations is looked up in the wrong database

I am trying to use a secondary database connection for some of my migrations in the following way:
# app/models/staging/migration.rb
class Staging::Migration < ActiveRecord::Migration
def self.connection
ActiveRecord::Base.establish_connection(:staging_db).connection
end
end
# db/migrate/<timestamp>_create_foo.rb
class CreateFoo < Staging::Migration
....
end
In my database.yml the staging_db connection is configured.
When I run rake db:migrate, the table foo is created correctly in the staging_db schema, and the table schema_migrations is created in the RAILS_ENV=development connection. However db:migrate reports the following error (which fails subsequent migrations):
Table 'staging_db.schema_migrations'
doesn't exist
Is there a way to tell Staging::Migration to look for the schema_migrations table in the current RAILS_ENV connection?
BTW, I am aware of the fact that staging_db is then not RAILS_ENV-aware. This is fine for me since every server has its environment configured through a separate database.yml which is not in my repo.
You should try do this before your first migration in the staging_db:
ActiveRecord::Base.connection.initialize_schema_migrations_table
This will create a schema migration table in the staging db. If this is not what you want you will have to manipulate some other things. The schema_migrations_table_name determines which table contains the migration versions:
def schema_migrations_table_name
Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
end
So if you have a table_name_prefix defined it will cause the schema_migration_table to look in the staging db.
I already did this but outside of rails, it should not be very different in rails, here is how I do it:
The first thing is to connect your database before your migrations are executed, in rails the best place may be in an initializer:
MyModel.establish_connection({
:adapter => "mysql2",
:database => "mydb",
:username => "root",
:encoding => 'utf8'
})
The hash will be usually loaded from an yml file but this is the result you want in the end.
MyModel can be an abstract class if you have multiple models in this database.
Next in your migration when you want to migrate this database you just have to do this:
class DoDomething < ActiveRecord::Migration
def self.connection
MyModel.connection
end
def self.up
add_column [...]
end
end
One thing to note when doing things this way is that there will be only one schema_migrations table and it will be in the "main" database.

Rake is out of synch with my Database

I have inherited a Ruby on Rails project where the programmer didn't use rake to create the db schema, so it seems very out of synch, is there a way to rectify this?
First create a schema.rb file
rake db:schema:dump
Then make a migration ot of it.
class CreateMigration < ActiveRecord::Migration
def self.up
# insert schema.rb here
end
def self.down
end
end
You might also need to create the schema_migrations table, and manually add the timestamp for this migration to it.

Rails DB Migration - How To Drop a Table?

I added a table that I thought I was going to need, but now no longer plan on using it. How should I remove that table?
I've already run migrations, so the table is in my database. I figure rails generate migration should be able to handle this, but I haven't figured out how yet.
I've tried:
rails generate migration drop_tablename
but that just generated an empty migration.
What is the "official" way to drop a table in Rails?
You won't always be able to simply generate the migration to already have the code you want. You can create an empty migration and then populate it with the code you need.
You can find information about how to accomplish different tasks in a migration here:
http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
More specifically, you can see how to drop a table using the following approach:
drop_table :table_name
Write your migration manually. E.g. run rails g migration DropUsers.
As for the code of the migration I'm just gonna quote Maxwell Holder's post Rails Migration Checklist
BAD - running rake db:migrate and then rake db:rollback will fail
class DropUsers < ActiveRecord::Migration
def change
drop_table :users
end
end
GOOD - reveals intent that migration should not be reversible
class DropUsers < ActiveRecord::Migration
def up
drop_table :users
end
def down
fail ActiveRecord::IrreversibleMigration
end
end
BETTER - is actually reversible
class DropUsers < ActiveRecord::Migration
def change
drop_table :users do |t|
t.string :email, null: false
t.timestamps null: false
end
end
end
First generate an empty migration with any name you'd like. It's important to do it this way since it creates the appropriate date.
rails generate migration DropProductsTable
This will generate a .rb file in /db/migrate/ like 20111015185025_drop_products_table.rb
Now edit that file to look like this:
class DropProductsTable < ActiveRecord::Migration
def up
drop_table :products
end
def down
raise ActiveRecord::IrreversibleMigration
end
end
The only thing I added was drop_table :products and raise ActiveRecord::IrreversibleMigration.
Then run rake db:migrate and it'll drop the table for you.
Warning: Do this at your own risk, as #z-atef and #nzifnab correctly point out, Rails will not be aware of these changes, your migration sequence fill fail and your schema will be different from your coworkers'. This is meant as a resource for locally tinkering with development only.
While the answers provided here work properly, I wanted something a bit more 'straightforward', I found it here: link
First enter rails console:
$rails console
Then just type:
ActiveRecord::Migration.drop_table(:table_name)
And done, worked for me!
You need to to create a new migration file using following command
rails generate migration drop_table_xyz
and write drop_table code in newly generated migration file (db/migration/xxxxxxx_drop_table_xyz) like
drop_table :tablename
Or if you wanted to drop table without migration, simply open rails console by
$ rails c
and execute following command
ActiveRecord::Base.connection.execute("drop table table_name")
or you can use more simplified command
ActiveRecord::Migration.drop_table(:table_name)
rails g migration drop_users
edit the migration
class DropUsers < ActiveRecord::Migration
def change
drop_table :users do |t|
t.string :name
t.timestamps
end
end
end
rake db:migrate
The simple and official way would be this:
rails g migration drop_tablename
Now go to your db/migrate and look for your file which contains the drop_tablename as the filename and edit it to this.
def change
drop_table :table_name
end
Then you need to run
rake db:migrate
on your console.
I wasn't able to make it work with migration script so I went ahead with this solution. Enter rails console using the terminal:
rails c
Type
ActiveRecord::Migration.drop_table(:tablename)
It works well for me. This will remove the previous table. Don't forget to run
rails db:migrate
I think, to be completely "official", you would need to create a new migration, and put drop_table in self.up. The self.down method should then contain all the code to recreate the table in full. Presumably that code could just be taken from schema.rb at the time you create the migration.
It seems a little odd, to put in code to create a table you know you aren't going to need anymore, but that would keep all the migration code complete and "official", right?
I just did this for a table I needed to drop, but honestly didn't test the "down" and not sure why I would.
you can simply drop a table from rails console.
first open the console
$ rails c
then paste this command in console
ActiveRecord::Migration.drop_table(:table_name)
replace table_name with the table you want to delete.
you can also drop table directly from the terminal. just enter in the root directory of your application and run this command
$ rails runner "Util::Table.clobber 'table_name'"
You can roll back a migration the way it is in the guide:
http://guides.rubyonrails.org/active_record_migrations.html#reverting-previous-migrations
Generate a migration:
rails generate migration revert_create_tablename
Write the migration:
require_relative '20121212123456_create_tablename'
class RevertCreateTablename < ActiveRecord::Migration[5.0]
def change
revert CreateTablename
end
end
This way you can also rollback and can use to revert any migration
Alternative to raising exception or attempting to recreate a now empty table - while still enabling migration rollback, redo etc -
def change
drop_table(:users, force: true) if ActiveRecord::Base.connection.tables.include?('users')
end
You can't simply run drop_table :table_name, instead you can create an empty migration by running:
rails g migration DropInstalls
You can then add this into that empty migration:
class DropInstalls < ActiveRecord::Migration
def change
drop_table :installs
end
end
Then run rails db:migrate in the command line which should remove the Installs table
The solution was found here
Open you rails console
ActiveRecord::Base.connection.execute("drop table table_name")
ActiveRecord::Base.connection.drop_table :table_name
if anybody is looking for how to do it in SQL.
type rails dbconsole from terminal
enter password
In console do
USE db_name;
DROP TABLE table_name;
exit
Please dont forget to remove the migration file and table structure from schema
I needed to delete our migration scripts along with the tables themselves ...
class Util::Table < ActiveRecord::Migration
def self.clobber(table_name)
# drop the table
if ActiveRecord::Base.connection.table_exists? table_name
puts "\n== " + table_name.upcase.cyan + " ! "
<< Time.now.strftime("%H:%M:%S").yellow
drop_table table_name
end
# locate any existing migrations for a table and delete them
base_folder = File.join(Rails.root.to_s, 'db', 'migrate')
Dir[File.join(base_folder, '**', '*.rb')].each do |file|
if file =~ /create_#{table_name}.rb/
puts "== deleting migration: " + file.cyan + " ! "
<< Time.now.strftime("%H:%M:%S").yellow
FileUtils.rm_rf(file)
break
end
end
end
def self.clobber_all
# delete every table in the db, along with every corresponding migration
ActiveRecord::Base.connection.tables.each {|t| clobber t}
end
end
from terminal window run:
$ rails runner "Util::Table.clobber 'your_table_name'"
or
$ rails runner "Util::Table.clobber_all"
Helpful documentation
In migration you can drop table by:
drop_table(table_name, **options)
options:
:force
Set to :cascade to drop dependent objects as well. Defaults to false
:if_exists
Set to true to only drop the table if it exists. Defaults to false
Example:
Create migration for drop table, for example we are want to drop User table
rails g migration DropUsers
Running via Spring preloader in process 13189
invoke active_record
create db/migrate/20211110174028_drop_users.rb
Edit migration file, in our case it is db/migrate/20211110174028_drop_users.rb
class DropUsers < ActiveRecord::Migration[6.1]
def change
drop_table :users, if_exist: true
end
end
Run migration for dropping User table
rails db:migrate
== 20211110174028 DropUsers: migrating ===============================
-- drop_table(:users, {:if_exist=>true})
-> 0.4607s
the best way you can do is
rails g migration Drop_table_Users
then do the following
rake db:migrate
Run
rake db:migrate:down VERSION=<version>
Where <version> is the version number of your migration file you want to revert.
Example:-
rake db:migrate:down VERSION=3846656238
Drop Table/Migration
run:-
$ rails generate migration DropTablename
exp:- $ rails generate migration DropProducts
if you want to drop a specific table you can do
$ rails db:migrate:up VERSION=[Here you can insert timestamp of table]
otherwise if you want to drop all your database you can do
$rails db:drop
Run this command:-
rails g migration drop_table_name
then:
rake db:migrate
or if you are using MySql database then:
login with database
show databases;
show tables;
drop table_name;
If you want to delete the table from the schema perform below operation --
rails db:rollback

Resources