I'm fairly new to Ruby on Rails here.
I have 2 migrate files that were provided. The first one, prefixed with 001, creates a table and some columns for that table. The next migrate file, prefixed with 002, inserts rows into the table created in file 001.
Running the migration (rake db:migrate in command line) correctly creates the table but doesn't insert any of the data which is the problem. The code from the insertion looks like this (except with a lot more Student.create statements,
class AddStudentData < ActiveRecord::Migration
def self.up
...
Student.create(:name => "Yhi, Manfredo", :gender => "M")
...
end
def self.down
Student.delete_all
end
end
My understanding is that Student is a model object, so my Student model looks like this,
class Student < ActiveRecord::Base
end
Do I need to explicitly define a create method in Student or is that something that's given? (This project was made using scaffold)
Thanks.
Edit 1: I used Damien's suggestion and called create! instead of create but got the same response. Then what I did to see whether the code was even reaching that far was call this,
Student.create12312313!(:name => "foo", :gender => "M")
which is obviously invalid code and the migrate didn't throw any error.
Edit2: Answer found. The schema_migrations table had its version set to 3, and I only had 3 different migration files so it never ran any of the migration files I had. That's why nothing was ever updating, and the bogus creates I used were never throwing errors. The reason the student data wasn't inserted the first time was because a certain table was already in the database and it caused a conflict the first time I migrated. So what I was really looking for wasn't db:migrate but rather db:reset Several hours well spent.
The create method is inherited from ActiveRecord::Base.
So no, you don't need to define it.
One reason why your datas could not be included would be that you have validations that doesn't pass.
You can easily see the error making your datas not being included by using create! instead of create.
So if the model can't be created, an exception will be thrown and the migrations will fail.
You may want to look at Data Seeding in rails 2.3.4. And is your rails migrations really running 001_create_whatever.rb? or were you just using that as an example? since 2.2.2 (iirc) migrations have been using timestamps such as 10092009....create_whatever.rb
How old is your rails version?
The migrations won't run if their schema number is in the database.
For older versions of rails, there will be a single row with the highest migration performed in it.
For newer versions, every migration gets a unique time-stamp as its version number, and its own row in schema_migrations when it gets added.
Related
I'm trying to run this migration, and the goal is to not have any server downtime while adding new columns. Since these columns have no relationships to other tables, exist in the code, or are required, I assumed this would be possible. But, after running the migration I observed UnknownAttributeError's being thrown when instantiating a new instance of the model.
The error:
Controller/existing_table/some_method::ActiveRecord::UnknownAttributeError
/code/app/models/existing_table.rb:40:in `new’
The line from the error:
e = ExistingTable.new(existing_table.attributes.except("id"))
The migration:
class AddTestFieldToExistingTable < ActiveRecord::Migration
def change
add_column :existing_table, :test_field, :integer
end
end
When I go to recreate the issue by opening up a console and executing lines similar as above, I don't get the error. My guess is that there is some attributes cached and we do need to have downtime while running these "passive" migrations.
I wonder if the 'existing_table' is still in memory, doesn't have the 'test_field' attribute, and when creating a new instance of ExistingTable it doesn't know how to assign the field. Since I cannot recreate it without starting a new instance, I would be guessing on an alternative solution of re-writing the ExistingTable constructor.
Is there a way to avoid restarting the rails instance or re-writing this code? Am I on the right track?
I tried to replicate your doubt, and I did not get the error, besides, everything worked as expected.
I am using Rails 7.0.1
First, I started puma:
rails s -b 0.0.0.0
Then create the migration:
rails g migration AddPassiveToGiro passive:boolean
The migration looks like this:
class AddPassiveToGiro < ActiveRecord::Migration[7.0]
def change
add_column :giros, :passive, :boolean
end
end
On rails console I created a new record:
Giro.create giro: 'delete me', passive: true
Then I modify the view where I list those records (using haml)
.list-item-description
= giro.passive.blank? ? 'not passive' : 'is passive'
And everything worked fine, no errors.
Rails load the tables' columns only once when the Rails server is started.
Depending on your setup and how you run your migration at deployment it might be possible that the server or at least one server instance started before the migration was done. Then that server instance will not see the newly added column until the next server restart.
To be safe you need to make sure that the migration ran successfully before you deploy the code changes using that change. Depending on your server setup that might mean that you need two different deployments: One with the migration and then later another with the code changes.
I am currently working on a rails app where we are using mongoid/mongoDB on the back-end. I understand that I don't need ActiveRecord like migration to migrate the schema, but I do need to migrate data as I change mongoid model definitions. Is anyone else out there running into the same scenario, if so how are you handling it?
Even though you're not making schema changes, you may need to move data between fields, or remove fields that are no longer used in the codebase. It's nice to have migrations that you can run when you deploy new code. I recommend using a gem called mongoid_rails_migrations. This provides you with migration generators like you're used to and provides some organization to migrating data.
class MyMigration < Mongoid::Migration
def self.up
MyModel.all.each do |model|
# label was renamed to name
model.set :name, model[:label] # copy the data from the old field to the new one
model.remove_attribute :label # remove the old field from the document
model.save!
end
end
end
Write a custom rake task to migrate the data as needed
This question addresses the same issue of creating custom migrations in a mongoid setup.
Runtime changing model with mongodb/mongoid
I had the some scenario recently, where I have to do some data migration only once (basically update dirty data);
So what I did have a mongoid migrations in /db/migrate/ and override the db:migrate task so that it creates a collection in mongo db of that app itself, say "migrations", that record the migration that got fired, with that, none of the migration will run again, and you can keep adding migrations with some hierarchy (if in case migration is interdependent).
How does one create a Rails migration properly so that a table gets changed to MyISAM in MySQL? It is currently InnoDB. Running a raw execute statement will change the table, but it won't update db/schema.rb, so when the table is recreated in a testing environment, it goes back to InnoDB and my fulltext searches fail.
How do I go about changing/adding a migration so that the existing table gets modified to MyISAM and schema.rb gets updated so my db and respective test db get updated accordingly?
I didn't find a great way to do this. You could change your schema.rb like someone suggested and then run: rake db:schema:load, however, this will overwrite your data.
The way I did it was (assuming you are trying to convert a table called books):
Save the existing data from the CLI: CREATE TABLE tmp SELECT * FROM books;
In your new migration file, drop the books table and recreate it with :options => "ENGINE=MyISAM" like someone said in the comment
Copy the contents back: INSERT INTO books SELECT * FROM tmp
i think that if you change your schema format (config.active_record.schema_format) from :ruby to :sql, all sql will be saved there.
i'd do some tests on a fresh app first if i were you, see how it works.
You can run any sql in migrations. This worked for me:
class ChangeMapOnlyUsersEngine < ActiveRecord::Migration[5.1]
def change
MyModel.connection.execute("ALTER TABLE my_models ENGINE = 'MyISAM';")
end
end
When I did this in the other direction (InnoDB -> MyISAM) it worked fine, without loss of data so I don't think it's neccesary to create temporary tables or similar. Note that MyISAM doesn't support transactions, so any tests against the database for a corresponding ActiveRecord model will be persisted, with a risk of test pollution.
I have a bunch of rails models that I'm re-writing into a single model to simplify my code and reduce unnecessary tables.
I'm wondering what the best way to delete a model class and its table is. I want past migrations to still succeed, but I don't want to leave the empty models lying around.
Do I have to manually delete the old migrations that reference these models, then manually delete the class files?
Does anyone have any tips for the best way to do this?
All in one solution.
Run the following commands:
rails destroy model ModelName
rails g migration DropTableModelName
The former will generate a new migration file which should looks like this:
class DropTableModelName < ActiveRecord::Migration
def change
drop_table :model_name
end
end
Now run db:migrate and you're done.
If you'd like to completely get rid of of a model and its table do this:
rails destroy model Name
The question is a bit stale now, but I just did:
rails destroy scaffold <ModelName> -p
The -p flag shows "pretend" output, which is good for seeing what will happen. Remove the '-p' flag and the results will match the output. This cleaned the entire collection of M-V-C files + testing + js files + the original migration, no problem.
I guess if you are one who likes to hand edit your migrations and include multiple steps in each, losing the original migration could break db:setup, so buyer beware. Keeping one action == one migration file should avoid this potential snafu.
What about doing ruby script/destroy model? That should take care of the model and the migration.
Depending on how far into development or production you are, you may want to migrate the models out safely using a migration to remove/backup data or what not. Then as bobbywilson0 suggested, using
script/destroy model
or if you rspec anything
script/destroy rspec_model
This will remove any spec tests as well.
Or you can always just drag them to the trash folder.
You can take a look at this one at rails guide.
But I suggest, if it is possible, you should delete the models and all references to the models. This will probably save time later as you don't need to maintain the dead code in the codebase.
If you'd rather have a manual based answer:
First run the following command to identify which migrations you want removed:
rake db:migrate:status
Feel free to grep -i on it too if you're confident of your naming scheme.
Make note of all the "add x to model name" and similar alterations to your Model. These can be removed using:
rails d migration AddXToModelName
Do this for every migration besides the initial create migration. The following command will take care of the initial create migration and the files associated with the model:
rails d model ModelName
I have a rails project that uses an old versions of the FlexImage plug-in to handle images.
In the old version that image data was stored in a column called "data", in the new version that column must be named "image_file_data".
I wrote a migration to rename the column, but when I try to run the migration, my guess is that rails tries to load the models, which then automatically check to see if the valid column is there (which it isn't) and that throws an error which halts the migration.
I would guess that my problems would be solved if I never loaded the model classes in question and just wrote some sql to rename the columns. But the following line doesn't work, since rails still tries to load the model.
Apartment.connection.execute "ALTER TABLE logos CHANGE DATA image_file_data MEDIUMBLOB;"
Oops, I figured it out. I was calling
model = (table_name.to_s).classify.constantize
Earlier, and this was causing the model to load