Reputation: 1361
class Agents << ActiveRecord::Base
belongs_to :customer
belongs_to :house
end
class Customer << ActiveRecord::Base
has_many :agents
has_many :houses, through: :agents
end
class House << ActiveRecord::Base
has_many :agents
has_many :customers, through: :agents
end
How do I add to the Agents
model for Customer
?
Is this the best way?
Customer.find(1).agents.create(customer_id: 1, house_id: 1)
The above works fine from the console however, I don't know how to achieve this in the actual application.
Imagine a form is filled for the customer that also takes house_id
as input. Then do I do the following in my controller?
def create
@customer = Customer.new(params[:customer])
@customer.agents.create(customer_id: @customer.id, house_id: params[:house_id])
@customer.save
end
Overall I'm confused as to how to add records in the has_many :through
table?
Upvotes: 108
Views: 115221
Reputation: 10582
Preface
This is a strange scenario and I hesitated to answer. It seems like
Agent
s should have manyHouse
s rather than a one-to-one relationship, and aHouse
should belong to just oneAgent
. But with that in mind....
"The best way" depends on your needs and what feels most comfortable/readable to you. Confusion comes from differences in ActiveRecord's behavior of the new
and create
methods and the <<
operator, but they can all be used to accomplish your goal.
new
Methodnew
will not add an association record for you. You have to build the House
and Agent
records yourself:
# ...
house = @cust.houses.new(params[:house])
house.save
agent = Agent.new(customer: @cust house: house)
agent.save
Note that @cust.houses.new
and House.new
are effectively the same because you still need to create the Agent
record in both cases.
(This code looks weird, you can't easily tell what it's supposed to be doing, and that's a smell that maybe the relationships are set up wrong.)
<<
OperatorAs Mischa mentions, you can also use the <<
operator on the collection. This will only build the Agent
model for you, you must build the House
model:
house = House.create(params[:house])
@cust.houses << house
agent = @cust.houses.find(house.id)
create
Methodcreate
will build both House
and Agent
records for you, but you will need to find the Agent
model if you intend to return that to your view or api:
house = @cust.houses.create(params[:house])
agent = @cust.agents.where(house: house.id).first
As a final note, if you want exceptions to be raised when creating house
use the bang operators instead (e.g. new!
and create!
).
Upvotes: 84
Reputation: 43298
I think you can simply do this:
@cust = Customer.new(params[:customer])
@cust.houses << House.find(params[:house_id])
Or when creating a new house for a customer:
@cust = Customer.new(params[:customer])
@cust.houses.create(params[:house])
You can also add via ids:
@cust.house_ids << House.find(params[:house_id])
Upvotes: 177
Reputation: 59509
Another way to add associations is by using the foreign key columns:
agent = Agent.new(...)
agent.house = House.find(...)
agent.customer = Customer.find(...)
agent.save
Or use the exact column names, passing the ID of the associated record instead of the record.
agent.house_id = house.id
agent.customer_id = customer.id
Upvotes: 6