New to rails and having some Heroku troubles. I created a table and with it an index. I needed to change a field name, and naturally the index goes with. So I created three migrations, one to remove the field, the next to remove the index, and the last to add both. My computer has no issues handling this migration, but Heroku fails.
I have tried merging migrations #1 and #2 into one migration, but had no luck with that.
Index name 'index_updateresults_on_env_id_and_created_at' on table 'updateresults' does not exist/app/vendor/bundle/ruby/2.1.0/gems/activerecord-4.0.0/lib/active_record/connection_adapters/abstract/schema_statements.rb:825:in `index_name_for_remove'
Edit:
May as well add my migrations.
class CreateUpdateresults < ActiveRecord::Migration
def change
create_table :updateresults do |t|
t.string :content
t.integer :env_id
t.timestamps
end
add_index :updateresults, [:env_id, :created_at]
end
end
class RemoveEnvIdFromUpdateresults < ActiveRecord::Migration
def change
remove_column :updateresults, :env_id, :string
remove_index :updateresults, [:env_id, :created_at]
end
end
class AddEnvNameToUpdateresults < ActiveRecord::Migration
def change
add_column :updateresults, :env_name, :string
add_index :updateresults, [:env_name, :created_at]
end
end
Edit 2
I'm at a loss. It seems as though in Postgre, removing a field also removes any indexes on that field. This may not happen in SqlLite, hence why my local environment works but Heroku breaks (just a guess). I've tried deleting the migration that removes the index to hopefully avoid this issue, I've committed the change, and pushed it to Heroku. Yet I'm still getting the same error, and when I clone the repo the migration still exists. Maybe this is something Git related and less Rails related.
Found a lot of information at this other question. I ended up being right in that it was git related. Completely unsure why my commits that were deleting the file, weren't taking place on Heroku. I ultimately did a git rm commitname, then heroku push -f to my repository. It finally took at this point.
Related
So I have this migration inside of my Rails project:
class CreateSettings < ActiveRecord::Migration[5.2]
def change
create_table :settings do |t|
t.string :frequency
t.text :emails, array: true, default: [].to_yaml
t.integer :reorder
t.timestamps
end
end
end
The default value was originally failing it so I created another migration in order to remove that default value:
class ChangeDefaultColumnForSetting < ActiveRecord::Migration[5.2]
def change
change_column_default(:settings, :emails, nil)
end
end
The schema now looks good and that default value is gone but when I push it up to Heroku and run heroku run rails db:migrate, it fails at the original CreateSettings migration since it still includes the default value. Even if I remove that default value from the first migration manually, I get a "Expected 1 argument but got 0" error inside of Heroku.
Any ideas on how I can go about this? The migration works in development so it must just be a Postgres problem (as I'm using SQLite in dev).
A quick fix is to delete the ChangeDefaultColumnForSetting migration and edit CreateSettings to say:
t.text :emails, array: true, default: []
or
t.text :emails, array: true, default: nil
Then commit the changes and push to Heroku.
After that you really want to stop using SQLite if you're deploying on PostgreSQL. Developing on top of SQLite and deploying on PostgreSQL is going to cause all kinds of problems. You really need to develop, test, and deploy with the same database. Do yourself a big favor and install PostgreSQL locally so that you can test and develop with the same database that you're deploying with.
Alternatively, you could look at Rails.env in your migrations and run different code in different environments. But really, this is a bad idea.
I have a POINT defined in my migrations... when I run the migrations I get a POINT in my database.. Perfect!! Just exactly what I asked for.
But.. What I see in schema.rb is t.string? This messes up my CI system, because it builds the database based on the schema.rb.
Now my co-worker wants to change the type in the database to array, because it would be easy. I find that leaves a stink in my code.
Q: How do I make the schema.rb file show the column as a point?
For completeness
/20141120 ... .rb
class AddHeaderToUser < ActiveRecord::Migration
def change
add_column :users, :header_image_centre, :point
end
end
/schema.rb
t.string "header_image_centre", limit: nil
This seems to be fixed in Rails 4.2
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.
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
I've got a problem trying to rollback one of my migration. It seems as if Rails is generating a temporary table for the migration, with temporary indices. My actual index on this table is less than 64 characters, but whenever Rails tries to create a temporary index for it, it turns into a name longer than 64 characters, and throws an error.
Here's my simple migration:
class AddColumnNameToPrices < ActiveRecord::Migration
def self.up
add_column :prices, :column_name, :decimal
end
def self.down
remove_column :prices, :column_name
end
end
Here's the error I'm getting:
== AddColumnNameToPrices: reverting ============================================
-- remove_column(:prices, :column_name)
rake aborted!
An error has occurred, this and all later migrations canceled:
Index name 'temp_index_altered_prices_on_column_and_other_column_and_third_column' on table 'altered_prices' is too long; the limit is 64 characters
I've changed the column names, but the example is still there. I can just make my change in a second migration, but that still means I can't rollback migrations on this table. I can rename the index in a new migration, but that still locks me out of this single migration.
Does anyone have ideas on how to get around this problem?
It looks like your database schema actually has index called prices_on_column_and_other_column_and_third_column. You have probably defined the index in your previous play with migrations. But than just removed index definition from migrations.
If it is true you have 2 options:
The simplier one (works if you code is not in production). You can
recreate database from scratch using migrations (not from
db/schema.rb) by calling rake db:drop db:create db:migrate. Make sure that you do not create this index with long name in other migration files. If you do, add :name => 'short_index_name' options to add_index call to make rails generate shorter name for the index.
If you experience this problem on a production database it is a bit more complicated. You might need to manually drop the index from the database console.
Had this problem today and fixed it by changing the migration to include the dropping and adding of the index causing the long name issue. This way the alteration is not tracked while I am altering the column type (that is where the really long name is caused)
I added the following:
class FixBadColumnTypeInNotifications < ActiveRecord::Migration
def change
# replace string ID with integer so it is Postgres friendly
remove_index :notifications, ["notifiable_id","notifiable_type"]
change_column :notifications, :notifiable_id, :integer
# shortened index name
add_index "notifications", ["notifiable_id","notifiable_type"], :name => "notifs_on_poly_id_and_type"
end
end