12

我正在学习 RoR 并尝试使用 Accepts_nested_attributes_for 和 has_and_belongs_to_many 来提交传统上是两种形式的信息。我在一些网站上读过它们兼容,一些网站不兼容,还有一些网站不知道。作为参考,我使用的是 Rails 2.3.4。我尝试从 Ryan 的 Scraps嵌套模型教程中对我的解决方案进行建模

从我尝试调试的内容来看,我似乎有两个问题,但我不知道为什么。

  1. 当我提交带有嵌套模型的表单时,只发布了部分嵌套模型信息。我只得到第一个字段,而不是用户可能选择的其他“n”个字段
  2. 在发布的单个字段中,没有任何行插入到我为 HABTM 关系创建的连接表中。

这是我的插入尝试的一段代码和相应的日志:

律师模特:

class Attorney < ActiveRecord::Base 
  has_and_belongs_to_many :associations
  accepts_nested_attributes_for :associations, :reject_if => proc { |a| a['name'].blank? }
end

关联模型:

class Association < ActiveRecord::Base
  has_and_belongs_to_many :attorneys
  accepts_nested_attributes_for :attorneys
  validates_presence_of :name, :message => "Please enter an association name."
end

律师总监:

def new
  @attorney = Attorney.new
  @attorney.associations.build

  respond_to do |format|
    format.html # new.html.erb
    format.xml  { render :xml => @attorney }
  end
end

def create
  @attorney = Attorney.new(params[:attorney])

  respond_to do |format|
    if @attorney.save
      flash[:notice] = 'Attorney was successfully created.'
      format.html { redirect_to(@attorney) }
      format.xml  { render :xml => @attorney, :status => :created, :location => @attorney }
    else
      format.html { render :action => "new" }
      format.xml  { render :xml => @attorney.errors, :status => :unprocessable_entity }
    end
  end
end

律师新观点:

<% form_for(@attorney, :html => {:multipart => true}) do |f| %>
  <%= f.error_messages %>
 <%= f.label :"First name" %> 
 <%= f.text_field :firstname %><br>

 <%= f.label :"Last Name" %> 
 <%= f.text_field :lastname %><br>

 <%= f.label :"Attorney Type" %> 
 <%= f.collection_select :member_type_id, MemberType.all, :id, :name %><br>

 <%= f.text_area :bio, :cols => 70, :rows => 20 %><br><br>

 <%= f.label :"Attorney Location" %> 
 <%= f.collection_select :office_location_id, OfficeLocation.all, :id, :location %><br>

 <div id="associations">
      <%= render :partial => 'shared/membership' %>
 </div>
 <%= add_association_link "Add Association" %>
    <%= f.submit 'Create' %>
<% end %>

部分会员:

<div class="association">
  <% fields_for :associations do |assoc_form| %>
    <%= assoc_form.collection_select(:association_id, Association.find(:all), :id, :name, :include_blank => true) %>

<%= link_to_function "remove", "$(this).up('.association').remove()" %> <%= link_to '新关联', new_association_path %> <% end %>

律师助理链接:

def add_association_link(name)
  link_to_function name do |page|
    page.insert_html :bottom, :associations, :partial => 'shared/membership', :object => AssociationsAttorneys.new
  end
end

加入表迁移:

class CreateAssociationsAttorneys < ActiveRecord::Migration
  def self.up
    create_table :associations_attorneys do |t|
      t.references :attorney, :null => false
      t.references :association, :null => false
      t.timestamps
    end
  end

  def self.down
    drop_table :associations_attorneys
  end
end

日志捕获:

    Processing AttorneysController#new (for 127.0.0.1 at 2009-12-04 08:16:19) [GET]
Rendering template within layouts/default
Rendering attorneys/new
  [4;35;1mMemberType Load (0.4ms)[0m   [0mSELECT * FROM "member_types" [0m
  [4;36;1mOfficeLocation Load (18.6ms)[0m   [0;1mSELECT * FROM "office_locations" [0m
  [4;35;1mAssociation Load (0.6ms)[0m   [0mSELECT * FROM "associations" [0m
Rendered shared/_membership (3.5ms)
  [4;36;1mCACHE (0.0ms)[0m   [0;1mSELECT * FROM "associations" [0m
Rendered shared/_membership (1.5ms)
Rendered shared/_nav (0.6ms)
Rendered shared/_footer (0.1ms)
Completed in 149ms (View: 114, DB: 20) | 200 OK [http://localhost/attorneys/new]

Processing ApplicationController#index (for 127.0.0.1 at 2009-12-04 08:16:19) [GET]

Processing AttorneysController#create (for 127.0.0.1 at 2009-12-04 08:16:57) [POST]
  Parameters: {"commit"=>"Create", "authenticity_token"=>"Jh7aMCcOY7jUu/D1YtiCswg2n6iwqnS98VnVn46psp0=", "associations"=>{"association_id"=>"3"}, "attorney"=>{"birthstate"=>"Alabama", "office_location_id"=>"1", "birthdate"=>"December 3, 2009", "birthcity"=>"Test", "middlename"=>"Test", "lastname"=>"Testing", "image_temp"=>"", "member_type_id"=>"2", "firstname"=>"Test", "bio"=>"testing testing testing", "suffix"=>"", "email"=>"testing@test.com"}}
  [4;35;1mAttorney Load (15.6ms)[0m   [0mSELECT "attorneys".id FROM "attorneys" WHERE ("attorneys"."email" = 'testing@test.com') LIMIT 1[0m
  [4;36;1mAttorney Create (0.8ms)[0m   [0;1mINSERT INTO "attorneys" ("birthstate", "created_at", "birthdate", "office_location_id", "birthcity", "updated_at", "middlename", "lastname", "firstname", "member_type_id", "suffix", "bio", "image", "email") VALUES('Alabama', '2009-12-04 15:16:57', 'December 3, 2009', 1, 'Test', '2009-12-04 15:16:57', 'Test', 'Testing', 'Test', 2, '', 'testing testing testing', NULL, 'testing@test.com')[0m
Redirected to http://localhost:3000/attorneys/11
Completed in 150ms (DB: 16) | 302 Found [http://localhost/attorneys]

我可以看到 associations"=>{"association_id"=>"3"} 它只获得了我为特定人拥有的多个关联中的最后一个,并且没有在连接表中创建任何条目。在哪里可能我的代码出错了?

4

1 回答 1

28

你们两个在这里有问题,不幸的是其中一个被另一个掩盖了。

这两个问题都源于这部分观点:

<div class="association">
  <% fields_for :associations do |assoc_form| %>
    <%= assoc_form.collection_select(:association_id, Association.find(:all),
      :id, :name, :include_blank => true) %>

问题 1:您误解了 accept_nested_fields_for 的作用。

accept_nested_fields_for 用于创建和修改表单中的相关对象。它可用于填充连接表,这正是您想要做的。但是,对于 HABTM 关系,使用 Accepts_nested_fields_for 填充连接表是不可能的。如果您想创建一个将与新 Attorney 链接的新协会,则可以很好地使用 accept_nested_fields_for。或者,如果您有一个需要为每条记录附加信息的丰富连接模型。

问题 2:您没有将此表单中的字段链接到律师表。这是使用accepts_nested_fields_for 所必需的。

我们已经确定,accepts_nested_fields_for 不是您完成此操作所需要的,但是,您仍然没有将 select association_id 字段与表单相关联。这就是为什么设置了 params[associations][association_id] 而不是 params[attorney][associations][association_id]。

问题 3:表单结构对于你想要完成的事情来说都是错误的。

我有太多需要纠正的地方才能给出适当的分解。您最好查看complex-forms-example 存储库。这是accepts_nested_attributes_for 的一个工作示例,它不处理任何HABTM 关系,但它应该教会你所有你需要知道的事情。下面的更正代码是您需要的 90%。上面链接的复杂表单示例将教您填写 add_association_link 和 create_association_link 空白所需知道的内容。

校正涉及以下步骤:

  1. 创建一个连接模型,并将关系更改为多对一,接受连接模型上的嵌套属性。
  2. 就构建事物而言,对控制器进行微调。
  3. 将表单构建器对象传递给部分。
  4. 重写部分中的表单,使其专注于新创建的连接模型。

您可以通过以下更改来完成此操作。

class Attorney < ActiveRecord::Base
  has_many :attorney_associations
  has_many :associations, :through => :attorney_associations

  accepts_nested_attributes_for :attorney_associations, :reject_if => proc { |a| 
     a['association_id'].blank? }
  accepts_nested_attributes_for :associations, :reject_if => proc {|a|
     a['name'].blank?}
end

class AttorneyAssociations < ActiveRecord::Base
  belongs_to :attorney
  belongs_to :association
end

检察长:

def new
  @attorney = Attorney.new
  @attorney.associations.build
  @attorney.attorney_associations.build


  respond_to do |format|
    format.html # new.html.erb
    format.xml  { render :xml => @attorney }
  end
end

新律师观点:

<% form_for(@attorney, :html => {:multipart => true}) do |f| %>
  <%= f.error_messages %>
 <%= f.label :"First name" %> 
 <%= f.text_field :firstname %><br>

 <%= f.label :"Last Name" %> 
 <%= f.text_field :lastname %><br>

 <%= f.label :"Attorney Type" %> 
 <%= f.collection_select :member_type_id, MemberType.all, :id, :name %><br>

 <%= f.text_area :bio, :cols => 70, :rows => 20 %><br><br>

 <%= f.label :"Attorney Location" %> 
 <%= f.collection_select :office_location_id, OfficeLocation.all, :id, :location %><br>

 <div id="associations">
   <% f.fields_for :attorney_association do |aa_form| %>
     <%= render :partial => 'attorney_association', :locals => {:f => aa_form} %>
   <% end %>
   <%= add_association_link "Add Another Existing Association" %>
   <% f.fields_for :associations do |assoc_form| %>
     <%= render :partial => 'attorney', :locals => {:f => assoc_form} %>       
   <%= create_association_link, "Create a New Association for this Attorney" %>
 </div>



 <%= f.submit 'Create' %>
<% end %>

我假设 add_association_link 是一个 javascript 帮助程序,它创建一个链接来克隆一个空实例,该实例是 Membership 部分。create_association_link 是一个类似助手的占位符,它将为新关联添加一个部分。

律师协会部分:

  <div class="attorney_association">
    <%= f.collection_select(:association_id, Association.find(:all),
      :id, :name, :include_blank => true) %>
    <%= link_to_function "remove", "$(this).up('.attorney_association').remove()" %>
  </div>

协会部分:

  <div class="association">
    <%= f.label_for :name %>
    <%= f.text_field :name %>
    <%= link_to_function "remove", "$(this).up('.attorney_association').remove()" %>
  </div>
于 2009-12-04T17:59:46.750 回答