Rails Generate Has_Many Association

Rails generate has_many association

There is no column for a has_many relationship. A belongs_to is backed by a column which holds a foreign key.

So if you generate a scaffold: rails g scaffold Post

And then you generate another scaffold: rails g scaffold Comment post:references

Then rails will create a migration that adds a column named post_id to the Comment table and creates an index on it. For both tables, it creates foreign key constraints between comments(post_id) and posts(id). Rails will also add belongs_to :post in the Comment model.

At anytime you can add a has_many to a model as long as another model belongs_to the first model and has a migration with the foreign key column.

Rails generate model with 'has_many' association

I don't think there is a way around generating your Car scaffold and then generating a migration that adds its reference to the existing Wheel.

rails g scaffold Car with:attributes
rails g migration AddCarToWheels car:references

Rails has great documentation about migrations in general. I leave them here for reference :)

How do I specific belongs_to and has_many relationships when generating new models

When passed as a generator argument belongs_to in is just an alias of references which tells rails to create a column named blog_id which is a foreign key:

# rails generate model Post blog:belongs_to
class CreatePosts < ActiveRecord::Migration[5.0]
def change
create_table :posts do |t|
t.belongs_to :blog, foreign_key: true
t.timestamps
end
end
end

This is the actual database column that defines the relation between two tables.

It also adds the association to the model:

class Post < ApplicationRecord
belongs_to :blog
end

Why does'nt the same work for has_many?

The arguments for the model generators are attributes of the model. blog_id is an actual attribute backed by a database column.

has_many is not an attribute. It's a metaprogramming method which adds a posts method to your Blog instances. You need to add it manually to the model.

If you run rails g model Blog posts:has_many foo:bar Rails will actually create a migration with those attributes:

class CreateBlogs < ActiveRecord::Migration[5.0]
def change
create_table :blogs do |t|
t.has_many :posts
t.bar :foo

t.timestamps
end
end
end

Rails does not type check the arguments. Of course the migration won't actually run:

undefined method `has_many' for #<ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition:0x007fd12d9b8bc8>

If you have already generated the migration just remove the line t.has_many :posts and add has_many :posts to app/models/blog.rb.

Rails: How to create a has_many through association with an alias/class_name

Understanding :source option of has_one/has_many through of Rails should help you out

When you declare the source you're not declaring the class of the has_many relationship, but the name of the relationship you're using as the middle man. In your case, try:

has_many :curators, :through => :project_curators, :source => :author

As someone in the above post states:

Most simple answer - the name of the relationship in the middle

Rails association - how to add the 'has_many' object to the 'owner'

current_user.score_cards << score_card

OR

score_card.user = current_user
score_card.save

How to force a has_many association using a different column as foreign key

You need to specify a different primary key for the relationship if you wish to achieve what you are looking to do.

To clarify, this is not the same as changing the primary_key of the model. This way is only changing the primary key used by the relationship. Please see the bottom of this post for examples.

I changed the keys from both using custom_id and changed one to foo_id. This way you have a better idea of what is going on between the models. You can use both custom_id if you wish, but I would suggest keeping the rails norm of foo_id for the belongs_to association.

If you want to use both of custom_id you'll have to add some specific foreign_keys


Here are the models:

Foo

class Foo < ApplicationRecord
has_many :bars,
primary_key: :custom_id,
foreign_key: :foo_id
end

Bar

class Bar < ApplicationRecord
belongs_to :foo,
primary_key: :custom_id
end

The Migrations

CreateFoos

class CreateFoos < ActiveRecord::Migration[5.2]
def change
create_table :foos do |t|

t.integer :custom_id, index: {unique: true}
t.timestamps
end
end
end

CreateBars

class CreateBars < ActiveRecord::Migration[5.2]
def change
create_table :bars do |t|

t.integer :foo_id, index: true
t.timestamps
end
end
end

Here is the updated Test that should now be passing:

Test

require 'test_helper'

class BarTest < ActiveSupport::TestCase
test "the truth" do
foo = Foo.new(id: 1, custom_id: 100)
bar = Bar.new(foo: foo)

assert bar.foo_id == foo.custom_id
# bar.foo_id = 100
# foo.custom_id = 100
end
end

Examples

Foo.find(1) #<Foo id: 1, custom_id: 100>
Bar.first #<Bar id: 1, foo_id: 100>

Bar.first.foo = #<Foo id: 1, custom_id: 100>
Bar.first.foo == Foo.find(1) # true

As you can see, this method does not change the primary key of Foo itself. It changes the primary key the relationship between Foo and Bar uses. Bar is realated to foo via custom_id: 100, but foo is still found with it's id: 1 key, not its custom_id key.



Related Topics



Leave a reply



Submit