Rails has_many / accepts_nested_attributes_for造成混乱吗?

时间:2018-07-05 02:15:26

标签: ruby-on-rails

关于一件事,我有点困惑,那就是在控制器中创建了多个“ belongs_to”模型项。

基本上,我在说我们有Person模型,Person has_many Addressesaccepts_nested_attributes_for :addresses

的地方就看到了它

(假设“地址”只是一个文本字段,以表单的形式,我们最终将拥有一个3个地址)

在“新”控制器中,我通常会看到以下内容:

@person = Person.new
3.times { @person.addresses.build }

然后在“创建”控制器操作中,将接受所有3个字段(使用fields_for形式),然后进行正常的全部创建。通过类似的东西:

def person_params
 params.require(:booking).permit(:name,:email,:age, :addresses_attributes => [:address])
end

它会创建所有3个地址。我不明白它是如何工作的。正在构建3个不同的实例,然后以某种方式Rails只是“知道”如何将它们关联起来?

任何人都可以解释发生了什么事吗?

2 个答案:

答案 0 :(得分:1)

当您声明Person模型accepts_nested_attributes_for :addresses时,您真正要说的是我希望Rails在Person模型上为地址(多个)创建getter和setter实例方法。 这类似于其他关系。例如,如果您在Person上声明has_many:friends关系,则可以调用@person.friends(getter)或@person.friends.create(setter)。 视觉效果……

class Person < ActiveRecord::Base
  # this is the getter method
  def addresses
    # I can retrieve all of the associated addresses for a particular person
  end

  # this is the setter method
  def addresses_attributes=(attributes)
    # I can take in any number of addresses and assoicate them to a particular person
  end
end

当您允许在控制器中使用addresss_attributes时

params.require(:person).permit(:addresses_attributes => [:address])

想起来就像您在创建一个Person时允许调用addresses_attribues setter方法一样……setter方法接受从表单提交的地址(多个)并将它们与该特定人员相关联。但是,当您调用@ person.addresses时,您会注意到它返回一个ActiveRecord::Associations::CollectionProxy,这一点很重要。

在控制器中,当您说出

@person = Person.new
3.times { @person.addresses.build }

可以想像它是创建一个空的Person以及与该Person关联的3个空地址,只有通过提交表单调用create动作后,这些属性才会填充属性。

编辑:

嵌套属性的主要优点(与常规关联有所不同)是,它允许您使用一种形式来创建/修改父类(在这种情况下为Person),同时创建通过使用fields_for

/修改其相关属性(在本例中为地址)

“我想另一个令人困惑的部分是,rails如何知道此人有3个空地址?”

Rails并不能立即知道...这就是为什么您必须在控制器中显式实例化X个空地址的原因。因此,现在当您调用form.fields_for :addresses时,只有X量的内存可以存储X量的地址。

答案 1 :(得分:1)

accept_nested_attributes_for :addresses在您的Person模型中,定义了has_many :addresses关联的属性编写器。

现在,当您将表单提交到Rails应用程序时,参数可能看起来类似于以下内容:

"user"=>{"name"=>'John', "email"=>'john@domain.com',"age"=>"25"
addresses_attributes"=>{"0"=>{"address"=>"Street 1"}, "1"=>{"address"=>"Street 2"}, "2"=>{"address"=>"Street 2"}}}

如果仔细查看addresses_attributes参数,它看起来类似于hash,并且key中的每个hash代表一个address对象。这就是Rails确切知道必须创建3条address记录的方式。

如果您想更好地理解这一点,请阅读此rails documentation

已更新:

new操作中使用3.times { @person.addresses.build }预先实例化地址完全不是强制性的

并且硬编码地址数可能不是正确的方法。如果有人要存储5个地址而您的应用程序只允许3个地址怎么办?

因此,您可能可以使用Jquery并以更好的方式实现它。或者甚至可以使用cocoon gem。