如何在Rails中向has_many :through关联添加记录

108
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

如何为 Customer 模型添加到 Agents

这是否是最佳方式?

Customer.find(1).agents.create(customer_id: 1, house_id: 1)

在控制台中,上述方法可以正常工作。但是我不知道如何在实际应用程序中实现。

假设一个表单被填写了,该表单需要输入 house_id。那么我在我的控制器中要这样做吗?

def create 
  @customer = Customer.new(params[:customer])
  @customer.agents.create(customer_id: @customer.id, house_id: params[:house_id])
  @customer.save
end

总的来说,我不清楚如何在has_many :through表中添加记录?


你会把“create”函数存储在哪个控制器中? - blkpingu
3个回答

176

我认为你可以简单地这样做:

 @cust = Customer.new(params[:customer])
 @cust.houses << House.find(params[:house_id])

或者当为客户创建新房子时:

 @cust = Customer.new(params[:customer])
 @cust.houses.create(params[:house])

你也可以通过id添加:

@cust.house_ids << House.find(params[:house_id])

20
请注意:除非已保存了父级内容,否则无法创建关联的房屋。 - rikas
这一定是我遇到的解决这个问题最优雅的方案。给你点赞。 - Daniel Bonnell
@RicardoOtero 我猜我们可以使用 build 而不是 create - Karan
@Mischa,如果House.find(params[:house_id])为nill,我该如何处理错误?如果params[:house_id]为nil,我会得到TypeMismatch错误。我已经使用了rescue,但是否有更好的方法呢? - Vishal
1
我观察到在某些情况下使用<<运算符会进行两次插入。因此,create方法是最好的方式。 - Swaps
@Mischa,如何一次将多个房屋详情添加到特定客户中,上面的答案只显示了一个房屋指向一个客户。 - pbms

84

前言

这是一个奇怪的场景,我犹豫是否要回答。似乎Agent应该拥有多个House而不是一对一的关系,而且一个House应该只属于一个Agent。但是考虑到这一点....

“最好的方法”取决于您的需求以及您感觉最舒适/可读的方式。混淆来自于newcreate方法以及<<运算符在ActiveRecord行为中的差异,但它们都可以用来实现您的目标。

new方法

new不会为您添加关联记录。您必须自己构建HouseAgent记录:

# ...
house = @cust.houses.new(params[:house])
house.save
agent = Agent.new(customer: @cust house: house)
agent.save
请注意,@cust.houses.newHouse.new实际上是一样的,因为在两种情况下您仍然需要创建 Agent 记录。
(这段代码看起来很奇怪,您无法轻松地知道它应该做什么,这可能意味着关系设置错误。)

<< 运算符

正如Mischa提到的那样,您还可以在集合上使用<<运算符。这将仅为您构建Agent模型,您必须构建House模型:
house = House.create(params[:house])
@cust.houses << house
agent = @cust.houses.find(house.id)

创建方法create

create方法将为您构建HouseAgent的记录,但如果您想将其返回到视图或API,则需要查找Agent模型:

house = @cust.houses.create(params[:house])
agent = @cust.agents.where(house: house.id).first

最后提醒一下,如果你想在创建house时出现异常,请改用感叹号操作符 (例如:new!create!)。


2
这行代码 agent = @cust.houses.find(house.id) 是否应该改为 agent = @cust.agents.find(house.id)?在“新方法”中的agent变量与后面的示例中的agent不同。这可能会给在联接表上使用其他属性的人带来一些困惑。 - vaughan
你能详细说明如何从联合表Agents中检索数据而不出现N+1错误吗?例如,为给定客户显示所有房屋及其对应的经纪人。 - Ankita.P

6

另一种添加关联的方法是使用外键列:

agent = Agent.new(...)
agent.house = House.find(...)
agent.customer = Customer.find(...)
agent.save

或者使用确切的列名,将关联记录的 ID 传递而不是记录本身。

agent.house_id = house.id
agent.customer_id = customer.id

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接