I wanted to know what is the right way to update a Model.
for example, suppose I want to change a name of a data member from likes to numOfLikes.
this variable appears in the Model itself, but also in schema.rb and in db\migrate\XXX.rb
I changed manually those files and got this error:
undefined method `numOfLikes' for # Topic:0x3442d88
So, what is the right way doing it? (I am also asking about deleting a data member or adding one)
The best way is to run a migration to rename the column, which will update the schema.rb file.
You shouldn't be editing schema.rb directly, and the migration file only runs commands on the database, not affect the model if changed once run.
Schema.rb represents the state of the database schema, it doesn't control or change it by changing the contents of the file alone.
A new migration that contains:
rename_column :table_name, :likes, :numOfLikes
This will rename the column in the database, and will dump the database schema into schema.rb with the new attribute name.
Related
I'm using Rails and Postgres. I currently have a Like table created. I need to drop this table and re-create it. Here's what I've done so far.
1) Rename create_likes migration file to create_likes_old so there is no conflict with file names within migrations.
2) Change the name of the table inside the migration from create_table :likes to create_table :likes_old.
3) Generate new create_likes migration that creates a table called Like.
Currently I'm running into the following issue when I run rake db:migrate:
PG::DuplicateTable: ERROR: relation "likes" already exists
This makes it seems like the Like table was never renamed to LikeOld. What's wrong and how do I fix this?
Old migrations don't run when you change their content, and you should not rename them. Even if they did run, changing the create_table :likes to create_table :old_likes cannot possibly change the name of an existing table. That isn't what create_table does. At best, re-running that migration will now cause a new table to be created called old_likes, with no content, while leaving your old likes table unaffected. In actuality re-running that migration will simply fail, as it will attempt to undo the migration first, dropping the table old_likes which does not yet exist.
What you need to do is create a new migration called rename_likes_to_old_likes, which actually renames the table, using rename_table. Then, either:
delete the old migration entirely, so you can introduce a new migration with the same name
OR
create a new migration with a unique name such as create_new_likes_table or the like, and introduce your new table there.
Are migrations instructions that change a model? Will I make several migrations, or will there be one migration per table? For instance, let's say if I want to change "username" to "admin_username". Does this call for a migration? Then, let's say I decide I want to add "age". Do I have to make a new migration, or do I just add it to the aforementioned migration?
You can make the quantity of migrations you want. My advise, try to plan your project and see how your model needs to be in order to avoid migrations. The migration feature is a great way to add, delete, rename fields to your model among other things.
If you need to change the username of the model User then you run rails g migration renameUsernameInUsers and add the necessary code to change the name of that field in you recently created migration file.
If some minutes later you realise that you also need to add a field you can make a new migration with your needs and add a field or add multiple fields in just one migration.
Remember, in order to apply your migrations you need to run rake db:migrate. If you modify the code in your migration files already migrated with this command, it will not have effect in your model. You will need a new migration.
In conclusion, you can(will) make many migrations for a model.
To go further on this topic you can see this official page for migrations in rails.
In a Rails application, how can I migrate the changes I make in models? For instance, I know that if I create a model with command "rails g model Person name:string", a migration will be created as well. However, if after this step I go to the created model "Person" and add a new attribute, will this new attribute be automatically added to a migration for later persistance in database?
Or am I looking at this from the wrong side, and an attribute should be added to a migration, and then added to a model?
Regards
You can't really "add" an attribute to a model, you do that by creating the migration file and running it -- Rails figures out what attributes a model has based on what columns are in the database. However, you do need to add a line to the model to whitelist the attribute if you want to be able to update it via mass assignment. That's why you'll often see a line like this in activerecord models:
attr_accessible :name
But that's optional and not essential to adding the attribute.
To actually add the new attribute to your model, first create a migration with:
rails g migration AddAddressToPerson address:string
That will create the migration file in the db/migration/ directory. (The form “AddXXXToYYY” and “RemoveXXXFromYYY” are understood by rails to mean "add (or remove) a new column to the model XXX", see the documentation for details). In this case I've added an attribute named address which is a string, but you could change that to whatever you want it to be.
Then to actually update the database, you need to run the migration with rake:
rake db:migrate
Finally, if you want to allow mass assignment on that attribute, add the attribute to your list of arguments to attr_accessible:
attr_accessible :name, :address
That should do it.
If you are adding the new attribute with attr_accessor, you will not need to do anything with migrations, but your changes will not be stored in the database.
If you do want to persist your changes, you will need to add the attribute to your model using a migration. You can just create a text file, with the proper structure, migrations are nothing fancy, but it is a lot easier to generate on like this rails generate migration AddLastNameFieldToUsers. The contents of such a file might be adjusted to look like this:
class AddLastNameFieldToUsers< ActiveRecord::Migration
def change
add_column :users, :last_name, :string
end
end
You don't need to add attributes directly to the model. Rails (actually ActiveRecord) infers it automatically. For the list of attributes for the model class, AR looks for a table with a plural form of the model's name (if model is Order, then it will look for attributes in the orders table). It is part of the design feature called CoC - Convention over Configuration.
So you if you have to add an attribute, you have to create a migration to add that field into the column as mentioned in other answers.
I have a Active Record model "car", I would like to change the name of this model to "train" without changing functionalities inside, that's only change the name. Also, the table name should be changed to "trains".
Is there any rails command can do that at onece? Or I have to manually change the name in side the class or migration? If I have to change manually, it's gonna be complicated, because I have to also change other models which have associations to my "car" model.
Any good suggestions?
I figured out the following way:
1, generate migration file:
rails generate migration rename_cars_to_trains
edit the created migration file to:
class RenameCarsToTrains < ActiveRecord::Migration
def self.up
rename_table :cars, :trains
end
def self.down
rename_table :trains, :cars
end
end
rake db:migrate
After these steps, the table name changed from cars to trains, then, I have to manually change the controller and views names and the associations...
If you have any more efficient way, let me know...
I would recommend the following:
Change manually the Active Record model class into Train
Make migration to change the database table name from cars to trains
Make good search to change references from Car to Train.
If you have constantly the need to change database table names, you might want to reconsider naming the tables more abstact way. Like in this case you could have table called vehicles and have the "type" field specifying the type (for instance: car or train).
I used following steps to rename my model
In sublime text:
press cmd + shift + find and choose case-sensitive search (see left buttons). It will search word in whole project
search and replace 'Cars' to 'Trains'
search and replace 'Car' to 'Train'
search and replace 'car' to 'train'
rails generate migration rename_cars_to_trains
manually change following file names
cars_controller
car_helper
Model/ car.rb
all related files in test folder
change folder name in views: cars to trains
If you're using RubyMine, go into the model, right click the class name > refactor and change the name. RubyMine will refactor everything and create a new migration for the database as well.
I need to make changes to an in-use production database. Just adding a few columns. I've made the changes to the dev database with migrations. What is the best way to update the production database while preserving the existing data and not disrupting operation too much?
It's MYSQL and I will be needing to add data to the columns as well for already existing records. One column can have a default value (it's boolean) but the other is a timestamp and should have an arbitrary backdated value. The row counts are not huge.
So if I use migrations how do I add data and how do I get it to just do the two (or three - I add data -latest migrations on the production db when it wasn't initially built via migrations (I believe they used the schema instead)?
I always follow this procedure:
Dump prod database with mysqldump command
Populate dev / test database with dump using mysql command
Run migrations in dev / test
Check migration worked
Dump prod database with mysqldump command (as it may have changed) keeping backup on server
Run migrations on prod (using capristano)
Test migration has worked on prod
Drink beer (while watching error logs)
It sounds like you're in a state where the production db schema doesn't exactly match what you're using in dev (although it's not totally clear). I would draw a line in the sand, and get that prod db in a better state. Essentially what you want to do is make sure that the prod db has a "schema_info" table that lists any migrations that you >don't< ever want to run in production. Then you can add migrations to your hearts content and they'll work against the production db.
Once you've done that you can write migrations that add schema changes or add data, but one thing you need to be really careful about is that if you add data using a migration, you must define the model within the migration itself, like this:
class AddSomeColumnsToUserTable < ActiveRecord::Migration
class User < ActiveRecord::Base; end
def self.up
add_column :users, :super_cool, :boolean, :default => :false
u = User.find_by_login('cameron')
u.super_cool = true
u.save
end
def self.down
remove_column :users, :super_cool
end
end
The reason for this is that in the future, you might remove the model altogether, during some refactoring or other. If you don't define the user class on line "User.find_by_login..." the migration will throw an exception which is a big pain.
Is there a reason you are not using the same migrations you used in your dev environment?
Adding a column with add_column in a migration should be non-destructive: it will generate a "ALTER TABLE" statement. If you know what you're going to put into the columns once created, you can fill in the values within the migration (you may choose a less time-consuming alternative if the row counts are large).
Removing or altering the definition of a column is, I think, platform-dependent: some will allow deletion of a column in place, others will perform a rename-create-select-drop sequence of commands.
To get more specific, we need more information: what kind of migration are you looking at, what platform are you running on, do you need to set values as part of the migration? Stuff like that would help a lot - just edit the question, which will push it back up the list.