Do I need to re-migrate whenever I change the models in Rails? Can someone tell me what migration really does? I am reading it and I am somewhat confused with the difference between the stuff inside db/migrate and the stuff inside app/models.
For example, if I add a has_one realtionship inside my model, do I need to re-migrate this? Why?
If your database changes, use a migration. If you're just adding methods to your model, no need to have a migration.
Example:
We start out and we just have first_name, last_name. We want to store those in the database, so we have a migration that does:
/app/models/human.rb
# empty
/db/migrate/xxxxx.rb
add_column :humans, :first_name, :string
add_column :humans, :last_name, :string
Then we get married, so we want to track that
/app/models/human.rb
belongs_to :spouse
We need to have a spouse_id field in the database, so we need a migration
/db/migrate/xxxxx.rb
add_column :humans, :spouse_id, :integer
We then have a kid.... In fact, we were all kids at one point, but to keep it simple, we'll have Humans and Offspring
/app/models/offspring.rb
belongs_to :human
/db/migrate/xxxxx.rb
create_table ...
However, no need to add anything to the Human migration, since no tables change here. We do need to add:
/app/models/human.rb
has_many :offspring
If you want to be able to get at, easily, your first born, you'd just add a method to your model. No need for a migration here:
/app/models/human.rb
def first_born
offspring.first
end
Related
I have a question about rails 6 and model association with or without migrations.
Let me be more specific.
Let's say we want to place a foreign key inside users table which links to the id column of the courses table.
Most of the tutorials and courses i run into follow this route:
create a migration with add_column :users, :course_id, :integer
place has_one or has_many and belongs_to to the appropriate models.
And that's it.
My thought is, how about this:
create a migration with add_reference :users, :course, foreign_key: true
place the appropriate has_one/has_many and belongs_to to the apropriate models.
or is it the same?
Doesn't the second option create indexes as well whereas the first does not?
Which is a best practice?
add_reference is meant to be a shortcut as part of the rails convention, under the hood, it will call add_column and other options that you specify [you can check the code for rails-6 here]
while foreign keys are not required, they are considered a best practice because they guarantee referential integrity. you can read more about it here https://edgeguides.rubyonrails.org/active_record_migrations.html#foreign-keys
or is it the same? Doesn't the second option create indexes as well whereas the first does not?
having said that, your 2 migrations are not entirely the same even though they work the same (they achieve the same goal) - the second option will add an index by default unless you specify the option not to - I definitely agree with you about using add_reference as this is an easier and more foolproof shortcut
of course, you can also achieve that manually by using your first migration by adding an index and a foreign key as well
add_column :users, :course_id, :integer
add_index :users, :course_id # uniq or not
add_foreign_key :courses, :users
I have a has_many relation in my app. e.g. department has many users.
I want to covert it to a has_and_belongs_to_many relation.
As part of the migration I need to preserve the current relation between users and departments, meaning I have to move all the data to the new connecting table.
This is the migration I created:
class CreateUserDepartment < ActiveRecord::Migration
def change
create_table :users_departments do |t|
t.belongs_to :user
t.belongs_to :department
end
###############################################
# need to move the data to the new table here #
###############################################
remove_column :users, :sub_department_id
end
end
what is the best way to write the missing line?
If you must, you can use execute "your SQL". See this question:
How do I add some inserts in rails migration?
The main value of the "Don't" answer to that question is in explaining why you would not want to use your models to do this. Also, I'd be surprised if you can or would want to do this using change, you would probably need to use self.up.
I am new to rails, and created a custom migration to change my database structure using Rails Generate. Here is the command I issued: rails g migration users.
Now, in the file it created, I inputed:
class Users < ActiveRecord::Migration
def change
add_column :first_name
add_column :last_name
remove_column :name
end
end
When I run rake db:migrate nothing happens. What do I need to do to fix this?
It's not running at all? It's hard to say based on the info you gave. Perhaps you should try a migration with a more unique name? Something like:
rails g migration ConvertUsersNamesToSingleField
I'm not sure if it's cool to have two migrations with the same name. But with short generic names like Users that might be the problem here. And it usually can't hurt to have a verbose and descriptive migration name, for posterity and clarity.
This questions agree that migration with non unique names don't work: Rails migrations with the same name
But even when ran, this will raise errors. You need to include table names in those column calls, and you need to specify a type when creating fields.
class ConvertUsersNamesToSingleField < ActiveRecord::Migration
def change
add_column :users, :first_name, :string
add_column :users, :last_name, :string
remove_column :users, :name
end
end
I'm getting 'rake aborted! ... posts_count is marked readonly' errors.
I have two models: user and post.
users has_many posts.
posts belongs_to :user, :counter_cache => true
I have a migration which adds the posts_count column to the users table and then calculates and records the current number of posts per user.
self.up
add_column :users, :posts_count, :integer, :default => 0
User.reset_column_information
User.all.each do |u|
u.update_attribute( :posts_count, u.posts.count)
end
end
when I run the migration I get the error. This is pretty clear-cut, of course and if I remove the :counter_cache declaration from the posts model, e.g.
belongs_to :user
the migration runs fine. This obviously, does not make sense because you couldn't really implement it this way. What am I missing?
You should be using User.reset_counters to do this. Additionally, I would recommend using find_each instead of each because it will iterate the collection in batches instead of all at once.
self.up
add_column :users, :posts_count, :integer, :default => 0
User.reset_column_information
User.find_each do |u|
User.reset_counters u.id, :posts
end
end
OK, the documentation states:
Counter cache columns are added to the
containing model’s list of read-only
attributes through attr_readonly.
I think this is what happens: you declare the counter in the model's definition, thus rendering the "posts_count" attribute read-only. Then, in the migration, you attempt to update it directly, resulting in the error you mention.
The quick-and-dirty solution is to remove the counter_cache declaration from the model, run the migration (in order to add the required column to the database AND populate it with the current post counts), and then re-add the counter_cache declaration to the model. Should work but is nasty and requires manual intervention during the migration - not a good idea.
I found this blog post which suggests altering the model's list of read-only attributes during the migration, it's a bit oudated but you might want to give it a try.
I have
add_column :foos, :bar_id, :integer
but I wast to be able to do stuff like
#foo.bar.name
rather than
Bar.find(#foo.bar_id)
I think its done with t.references when creating a table... but how do a add it post-table-creating?
see migration guide here.
create_table :products do |t|
t.references :category
end
That lets you specify it in the migration, but you also need to use belongs_to, and either has_one or has_many attributes in the model. You could really do that without the migration to create it since you already have the foreign key id already setup. Just use the above mentioned attributes.
Also see this ActiveRecord guide.
You have to edit corresponding model file and add there for example belongs_to declaration. This is exactly what allows you to call the #foo.bar.name.
Why do you need to do that in a migration? Why can't you just add the bar_id field in the migration, than add an association in foo's model?
If you are using the Rails 4.x you can now generate migrations with references, like this:
rails generate migration AddUserRefToProducts user:references
like you can see on rails guides