I want to drop a table that has changed a few times (add columns, rename, add index..10 migration files)
If I do:
def up
drop_table :tablename
end
def down
create_table :tablename do |t|
t.string :string
...
end
end
On the down method, do I have to include all the columns as they were in the last state of the table? Or will rails be smart enough to remember its last state?
Thanks!
rails is smart, but its not the job of rails to remember the state of the tables that were dropped. So you have to make sure that you create the table with the columns that you would expect at this state.
Related
I created a model in my Rails app and realized after some time that I had forgotten to add some attributes, and later added them via a generated migration.
I now realize that the order of the attribute columns in schema.rb is the order that they will appear in a generated resource view in ActiveAdmin.
I want to reorder the columns when I view that model in ActiveAdmin, and the only way I have thought to do so is by changing the column order in the database.
I have looked here and here, and attempted to run a database migration using change_table or change_column. This had no resulting change.
This is migration I ran, which had no result:
class Reordercolumn < ActiveRecord::Migration[5.0]
def up
change_table :student_details do |t|
t.change :exact_length, :text, after: :length_of_stay
t.change :returned_home, :boolean, after: :spouse_name
t.change :has_spouse, :boolean, after: :expectation
end
end
end
Looking to view attribute columns in ActiveAdmin in a particular order, I ran a database migration to change columns, but the migration is not reordering the columns.
You need to reorder columns in ActiveAdmin? Let's do it in corresponding admin/student_detail.rb file
index do
selectable_column
column :exact_length
column :returned_home
column :has_spouse
end
show do
attributes_table do
row :title
row :returned_home
row :has_spouse
end
end
More info about customizing index and show views you can find in the docs
I have a migration that creates a table, and I need to access the model for that table to create another table. The migration seems to not recognized that the original table has been created, so to make sure I put a debugger in my code and when I get to the model call, it says User(Table doesn't exist) even though in mysql I see it being created.
Looks like migrations can't see the current state of the database, any ideas how to get around that?
Just to be more specific about my question:
I'm trying to use Archivist to create archive of my current User table so I have
class ArchivedHeuristicReviewsTable < ActiveRecord::Migration
def self.up
create_table "users" do |t|
t.string "name"
...
end
debugger
Archivist.update User
end
def self.down
drop_table :users
drop_table :archived_users
end
end
the Archivist, doesn't create the archived_user table, so when I stopped at debugger and did User, I got User(Table doesn't exist).
I even tried Archivist call in a newer migration, so to make sure User creation is all done, but it still didn't recognize the user table.
Any ideas?
This should do the trick:
User.connection.schema_cache.clear!
User.reset_column_information
Updated (to show code)
I'd like to mimic ActiveRecord's automatic timestamps directly in the database, but without explicitly adding this logic after every migration's create_table() call.
Here's what I do now:
class StatusQuo < My::Migration::Subclass
def self.up
create_table :tbl do |t|
... some columns ...
t.timestamps
end
add_default_now(:tbl, :created_at) # ALTER COLUMN ... DEFAULT NOW()
add_default_now(:tbl, :updated_at) # ALTER COLUMN ... DEFAULT NOW()
add_updated_at_trigger(:tbl) # BEFORE UPDATE ON ... trg_updated_at()
end
end
By contrast, here's what I'd like to do:
class Druthers < My::Migration::Subclass
def self.up
create_table :tbl do |t|
... some columns ...
t.timestamps
end
end
end
Is there an easy or recommended way to accomplish this? Using activerecord 3, postgresql 8.4.
Here's the best I could come up with so far, full source here:
In config/environment.rb check to see if our connection adapter is PostgreSQL. If so, require a file that does the following:
Wrap ColumnDefinition#to_sql to force defaults
Force "created_at" and "updated_at" to have DEFAULT CURRENT_TIMESTAMP
Wrap create_table to conditionally apply the trigger
If the newly created table has an "updated_at" column, install a trigger referencing a database FUNCTION assumed to exist.
Not pretty (need to have maintain a FUNCTION definition outside of this code) and not complete (change_table won't handle introducing timestamps properly), but good enough.
Let's say you have "lineitems" and you used to define a "make" off of a line_item.
Eventually you realize that a make should probably be on its own model, so you create a Make model.
You then want to remove the make column off of the line_items table but for every line_item with a make you want to find_or_create_by(line_item.make).
How would I effectively do this in a rails migration? I'm pretty sure I can just run some simple find_or_create_by for each line_item but I'm worried about fallback support so I was just posting this here for any tips/advice/right direction.
Thanks!
I guess you should check that the Make.count is equal to the total unique makes in lineitems before removing the column, and raise an error if it does not. As migrations are transactional, if it blows up, the schema isn't changed and the migration isn't marked as executed. Therefore, you could do something like this:
class CreateMakesAndMigrateFromLineItems < ActiveRecord::Migration
def self.up
create_table :makes do |t|
t.string :name
…
t.timestamps
end
makes = LineItem.all.collect(:&make).uniq
makes.each { |make| Make.find_or_create_by_name make }
Make.count == makes.length ? remove_column(:line_items, :make) : raise "Boom!"
end
def self.down
# You'll want to put logic here to take you back to how things were before. Just in case!
drop_table :makes
add_column :line_items, :make
end
end
You can put regular ruby code in your migration, so you can create the new table, run some code across the old model moving the data into the new model, and then delete the columns from the original model. This is even reversible so your migration will still work in both directions.
So for your situation, create the Make table and add a make_id to the lineitem. Then for each line item, find_or_create with the make column on lineitem, setting the returned id to the new make_id on lineitem. When you are done remove the old make column from the lineitem table.
I want a migration to create a clone of an existing table by just suffixing the name, including all the indexes from the original table.
So there's a "snapshots" table and I want to create "snapshots_temp" as an exact copy of the table (not the data, just the table schema, but including the indexes).
I could just copy and paste the block out of the schema.rb file and manually rename it.
But I'm not sure by the time this migration is applied if the definition from schema.rb will still be accurate. Another developer might have changed the table and I don't want to have to update my migration script.
So how might I get the schema of the table at runtime? Essentially, how does 'rake schema:dump' reverse engineer the table so I can do the same in my migration? (but changing the table name).
Try doing it with pure SQL. This will do what you want:
CREATE TABLE new_tbl LIKE orig_tbl;
In Rails 4 & PostgreSQL, create a new migration and insert:
ActiveRecord::Base.connection.execute("CREATE TABLE clone_table_name AS SELECT * FROM source_table_name;")
This will create the clone with the exact structure of the original table, and populate the new table with old values.
More info: http://www.postgresql.org/docs/9.0/static/sql-createtableas.html
This will do. It's not perfect, because it won't copy table options or indices. If you do have any table options set, you will have to add them to this migration manually.
To copy indices you'll have to formulate a SQL query to select them, and then process them into new add_index directives. That's a little beyond my knowledge. But this works for copying the structure.
class CopyTableSchema < ActiveRecord::Migration
def self.up
create_table :new_models do |t|
Model.columns.each do |column|
next if column.name == "id" # already created by create_table
t.send(column.type.to_sym, column.name.to_sym, :null => column.null,
:limit => column.limit, :default => column.default, :scale => column.scale,
:precision => column.precision)
end
end
# copy data
Model.all.each do |m|
NewModel.create m.attributes
end
end
def self.down
drop_table :new_models
end
end
Copy the tables entry from your projects db/schema.rb straight into your migration. Just change the table name and your good to go.
It looks like this logic is wrapped up in ActiveRecord::SchemaDumper but it only exposes an all-in-one "dump" method and you can't dump just a specific table (the "table" method is private).