has_many method does not work after renaming model and table - ruby-on-rails

I had modelA, which has_many modelBs and modelB belongs_to modelA. I tried to change the name of a modelB to modelC without really knowing what I'm doing. Before I could call #modelA.modelBs just fine and get a list of objects, but now I can't call #modelA.modelCs. I get a NoMethodError. These are the things I did to try to make the switch:
I renamed the table with a sqlite3 migration:
def change
rename :modelBs, :modelCs
end
Then I change modelA so that it has_many :modelCs and I renamed and edited modelB.rb (now modelC.rb)and changed the class name so that it's class ModelC < ActiveRecord::Base. It still belongs_to :modelA
Rails can't find a bars method to call on an instance of ModelA. I get a NoMethodError when I do #modelA.modelCs. I ran the migration, I restarted the server. What am I missing? Did I do a bad thing?

did the migration run successfully and did your table actually get renamed? I have always used rename_table for this:
def change
rename_table :modelBs, :modelCs
end
Inside your class, you can also tell rails which table the model should be associated with, just to test whether it's looking at the right table
class modelC < ActiveRecord::Base
table_name "modelCs"
end
hope that helps

What happens if you do a $rake db:reset? db:reset is supposed to rebuild the database with all of the seed data (without the migrations); maybe check this and see what the dependencies are like at this point, then you can run your $rake db:migrate to run the migrations and see what your left with there. Can you include more information about your backend? Is it Sqlite3? PostgreSQL?
db:migrate runs (single) migrations that have not been run yet.
db:reset does db:drop, db:setup

Related

Join table error on running migration

I have two models in rails 5.1
class Category < ActiveRecord::Base
has_and_belongs_to_many :sub_categories, join_table: "categories_join_table"
end
class SubCategory < ActiveRecord::Base
has_and_belongs_to_many :categories, join_table: "categories_join_table"
end
I have added multiple migrations the problem is when I try to run migration I get the error ERROR: relation "comfort_factor_sub_categories" does not exist because in the migration to create table comfort_factor_sub_categories will run in later migrations. How can I handle this?
Note: I can't change the name of join_table as it is just an example I have long names.
This question is over three years old at the time of this writing, but I just ran into the same problem so I thought I'd share my solution here.
In my case, I was running Rails 6.1, so my error message looked a bit different:
StandardError: An error has occurred, this and all later migrations canceled:
Could not find table 'officers_users'
The officers_users table is a join table that is created in a later migration, but the migration in question doesn't make any use of it, so why was I getting this error?
At first, I thought it might be a Rails bug as my migration was using update_columns to modify the users table, which shouldn't run any callbacks, but then I noticed that the values that I was updating them with were dependent on a computed attribute, which in turn was dependent on the officers_users join table. So Rails was right and I was wrong (once again, hah)! The solution was simply to make the failing migration self-sufficient without needing that computed attribute. Once I did that, everything was good again.
So if you run into the same problem, I would suggest checking your migration with a fine toothed comb and look for any hidden dependencies that might be using the later migration's join table.
If I understood your problem correctly, you have added several migrations and you cannot run them because some relationship is not found.
In that case, you should duplicate the classes in migrations:
class CreateCategories < ActiveRecord::Migration[5.1]
class Category < ActiveRecord::Base
# without declaring the relationship with the table which does not exist yet
end
def up
create_table :categories do |t|
# t.something
end
end
def down
drop_table :categories
end
end
You should then do the same thing for SubCategory.
For creating a proper join_table, you can refer to Rails official documentation

rails migration is not reflecting while migration is successful

I have created 2 tables (categories and products) and I did has_many association like Category has_many :products and Product belongs_to :category.
When I do a migration like:
rails generate migration add_product_id_to_categories product_id:integer
then migration is running but not seeing product_id in Category.
I've tried multiple ways like
add_product_id_to_category product_id:integer
but still facing the same problem.
You only generated a migration file (rails generate migration add_product_id_to_categories product_id:integer). You have to run the migration with rake db:migrate.
Generating a migration is a different thing than actually running it. You just generated it, and in order to execute what you have written, you need to run it, and you can do so by invoking the following command:
rake db:migrate
Not sure that either you have run the migration or not, run the following command, and it will tell you the statues of all the migrations. If the status is up, the migration has been run, and for down, a migration couldn't be run.
rake db:migrate:status
When you run a migration, and you would like to get notified in terminal that it has been successfully run, you can add a puts statement in there like following:
class AddColumnToCategories < ActiveRecord::Migration
def change
add_column :categories, :product_id, :integer, index: true
puts "product_id column has been added into categories."
end
end
Have you tried rails generate migration AddProductIdToCategories product_id:integer?
When you have created the migration if you look inside the migration file you can see if any code has been generated in the change method before you run the migration. If it has not generated any content you can manually add it:
def change
add_column :categories, :product_id, :integer
end

changing the association and deleting corresponding migration

I am new to ruby on rails. I have created a many-to-many association and a migration, such as follows:
in Foo model:
has_and_belongs_to_many bars
in Bar model:
has_and_belongs_to_many foos
and the migration for join table has the following content:
def change
create_table :foos_bars, id: false do |t|
t.references :foo
t.references :bar
end
end
Now I see that the association should be one-to-many as: foo has many bars.
I don't know if I have generated controllers after the above migration, but I know that the file of the above migration is the last one that was created in the migrate folder. Also I have not made any connection yet between any foo and any bar.
Can I do the following to start fresh (change the association and also remove the file of the migration):
use 'rake db:rollback'
delete the file of the join table migration (i.e. timestamp_create_bars_foos.rb file)
change the 'has_and_belongs_to_many' in foo to 'has_many', and in bar to 'belongs_to'
run: 'rails generate migration CreateFoosBars'
run: 'rake db:migrate'
If not, what can I do to make the above modification for the association?
Thanks in Advance!
The steps you have listed looks good.
The only thing that stands out is the intention to CreateFoosBars again in Step 4 : rails generate migration CreateFoosBars'.
Since the foos and bars tables are already created, and this is a has_many relationship, you just need to add a reference for foo in the bars table, and not create a new table.
Something along the lines of:
rails g migration add_foo_id_to_bar foo_id:integer
Would advise reviewing Rails 3 migrations: Adding reference column? to figure out what the migration should do.

How to reflect in the database a new belongs_to and has_many relationship in Ruby on Rails

I am new to rails (usually a python guy) and have just been trying to build a simple task manager application for fun. I am using Devise for authentication and have a single Task object I am trying to relate to a user. I have added the following to the Task model:
class Task < ActiveRecord::Base
belongs_to :user
end
and I have added the following in my User model for Devise:
class User < ActiveRecord::Base
has_many :tasks
<<normal Devise stuff>>
end
Whenever I added this information I then ran: rake db:migrate. It then gave me an error that the database field did not exist for user_id when I tried to do anything with it.
I am sure it is something rather simple that I am missing. Thanks for the help.
Adding a belongs_to (or any other) relationship to your model only tells active record that models are linked logically. This gives you access to methods like task.user. For this to actually work, the instances must be linked via database fields.
This is the step you're missing: you need to create a migration that will add a column to the Task table indicating which user it belongs to.
rails g migration AddUserIdToTasks user_id:integer
Note AddUserIdToTasks can be whatever name you want. It makes no difference. You can then open db/migrations/add_user_to_tasks and see what it does. Usually self.up will modify the database how you want it and self.down will do the opposite (so, in this case, remove the used_id).
Then to actually execute the SQL commands to alter the database table and schema, run
rake db:migrate
You need to generate a migration to add the foreign key first:
rails g migration AddUserIdToTasks user_id:integer
Then run db:migrate
And if you want the user to be able to reference the association as user.dreams, you need to add :class_name => 'Task' to the has_many line in the User model.
Your user class seems to be too dreamy to take care of it's tasks.
has_many :dreams // should be probably
has_many :tasks
Assuming that you tasks model has a user_id field, that is.

Rails model validators break earlier migrations

I have a sequence of migrations in a rails app which includes the following steps:
Create basic version of the 'user' model
Create an instance of this model - there needs to be at least one initial user in my system so that you can log in and start using it
Update the 'user' model to add a new field / column.
Now I'm using "validates_inclusion_of" on this new field/column. This worked fine on my initial development machine, which already had a database with these migrations applied. However, if I go to a fresh machine and run all the migrations, step 2 fails, because validates_inclusion_of fails, because the field from migration 3 hasn't been added to the model class yet.
As a workaround, I can comment out the "validates_..." line, run the migrations, and uncomment it, but that's not nice.
Better would be to re-order my migrations so the user creation (step 2) comes last, after all columns have been added.
I'm a rails newbie though, so I thought I'd ask what the preferred way to handle this situation is :)
The easiest way to avoid this issue is to use rake db:schema:load on the second machine, instead of db:migrate. rake db:schema:load uses schema.rb to load the most current version of your schema, as opposed to migrating it up form scratch.
If you run into this issue when deploying to a production machine (where preserving data is important), you'll probably have to consolidate your migrations into a single file without conflicts.
You can declare a class with the same name inside the migration, it will override your app/models one:
class YourMigration < ActiveRecord::Migration
class User < ActiveRecord::Base; end
def self.up
# User.create(:name => 'admin')
end
end
Unfortunately, your IDE may try to autocomplete based on this class (Netbeans does) and you can't use your model logic in there (except if you duplicate it).
I'm having to do this right now. Building upon BiHi's advice, I'm loading the model manually then redefining methods where I need to.
load(File.join(RAILS_ROOT,"app/models/user.rb"))
class User < ActiveRecord::Base
def before_validation; nil; end # clear out the breaking before_validation
def column1; "hello"; end # satisfy validates_inclusion_of :column1
end
In your migration, you can save your user skipping ActiveRecord validation:
class YourMigration < ActiveRecord::Migration
def up
user = User.new(name: 'admin')
user.save(validate: false)
end
end

Resources