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
Rails: Serializing Objects in a Database
Contact Form in Ruby, Sinatra, and Haml
Activerecord Objects in Hashes Aren't Garbage Collected -- a Bug or a Sort of Caching Feature
Using a Ruby Script to Login to a Website via Https
Error Installing Any Ruby Version with Rvm on Osx
Ruby Koans: Why Convert List of Symbols to Strings
Rails 5.2.0 with Ruby 2.5.1 Console - 'Warning:' 'Already' Initialized Constant Fileutils::Version
Array.Include? Multiple Values
Differencebetween a Process' Pid, Ppid, Uid, Euid, Gid and Egid
What Are the Ruby's Object#Taint and Object#Trust Methods
Difference Between .Nil, .Blank? and .Empty
Array Attribute for Ruby Model
Rails How to Switch Between Dev and Production Mode