Rails 3 序列化模型字段 form_for 和 field_for 未生成正确的名称

Rails 3 serialized model field form_for and field_for not generating correct name

我有这个型号:

class CompanyCrawler < ActiveRecord::Base
  .... 
  serialize :entry_pages, Array

  def entry_page_objects
    entry_pages.map { |url| EntryPage.new(url) }
  end

  def entry_page_objects_attributes=(attributes)
    # ...
  end
  ....
end

渲染模型的形式:

.....
%p
  %p
    = crawler_form.label 'Entry pages'
  = crawler_form.text_area :entry_pages_text, size: '80x6'
  %ul.entry-pages
    = crawler_form.fields_for :entry_page_objects do |entry_page_field|
      %li=entry_page_field.text_field :url, size: 80
    %a{href: '#', class: 'add-button'} Add Entry Page

我遇到的问题是表单错误地呈现了 entry_page_object 输入名称(例如 company_crawler[entry_page_objects_attributes][0][url] 而不是 company_crawler[entry_page_objects][0][url])。我真的不知道该怎么做,我已经阅读了文档,示例中说只要定义 attr_attributes=(attributes)persisted? 我就可以将 fields_for 用于集合,就像它们是用 accept_nested_fields.

定义的关联

我见过不同的解决方案,比如将 String 'entry_page_objects[]'fields_for,但我想与 rails 命名约定保持一致,我知道我可以使用 form_tag 而不是 form_for 但我想让 fields_for 按预期工作。

这里有一些信息供所有没有正确理解 nested_attributes 工作原理的人使用,比如我。

我报告的问题实际上是它应该如何工作。当我们有,比方说,这个模型:

class Foo < ActiveRecord::Base # it has name attribute
  has_many :larodis
  accepts_nested_attributes_for :larodi
end

class Larodi < ActiveRecord::Base # it has name attribute
  belongs_to :foo
end

这个定义让我可以通过提供参数的散列来创建具有许多 LarodiFoo。例如:

x = Foo.create(name: 'Josh', larodi_attributes: [ {name: 'Wayne'} ]
x.larodis.map(&:name) # ['Wayne']

现在是 #field_for 了解我们是否有嵌套属性的部分。我们通过寻找 name_attributes= 方法来检查这一点。如果它被定义 #fields_for 生成类型 <input ... name=object[name][INDEX][method]>... 的形式,其中索引只是一个整数。

请记住,在实现自定义 name_attibutes(attributes) 时,您必须检查属性类型 - 它可以是 Array 就像示例一样,它可以是 Hash 这种类型:

{ 1 => { ... } , 2 => { ... } }

就像表示数组的散列一样,其中键是索引,值是该索引的值。

答案看起来像这样:

_form.html.haml

....
= crawler_form.fields_for :entry_pages do |entry_page_field|
  %li
    =entry_page_field.text_field :url, size: 80
...

company_crawler.rb

class CompanyCrawler < ActiveRecord::Base
  ....
  serialize :entry_pages, Array

  def entry_pages_attributes=(attributes)
    self.entry_pages = attributes_collection(attributes).map do |attribute|
      EntryPage.new(attribute[:url])
    end
  end

  def entry_pages=(entry_pages)
    entry_pages = entry_pages.map do |entry_page|
      cast_entry_page_to_entry_page_object(entry_page)
    end

    write_attribute(:entry_pages, entry_pages)
  end

  ...

  private

  def attributes_collection(attributes)
    case attributes
    when Array
      attributes
    when Hash
      attributes.values
    end
  end

  def cast_entry_page_to_entry_page_object(entry_page)
    case entry_page
    when String
      EntryPage.new(entry_page)
    when EntryPage
      entry_page
    end
  end
end

为清楚起见,我删除了 entry_page_objects 并仅使用 entry_pages