尝试使用accepts_nested_attributes_for和has_and_belongs_to_many但未填充连接表

发布于 2024-08-13 07:03:13 字数 6100 浏览 8 评论 0原文

我正在学习 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('.关联').remove()" %> <%= link_to '新关联', new_association_path %> <%结束%>

律师助手链接:

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"=>"[email protected]"}}
  [4;35;1mAttorney Load (15.6ms)[0m   [0mSELECT "attorneys".id FROM "attorneys" WHERE ("attorneys"."email" = '[email protected]') 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, '[email protected]')[0m
Redirected to http://localhost:3000/attorneys/11
Completed in 150ms (DB: 16) | 302 Found [http://localhost/attorneys]

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

I am learning RoR and trying to use accepts_nested_attributes_for and has_and_belongs_to_many to submit information that would traditionally be two forms. I have read on some sites they are compatible, some sites they aren't compatible, and some sites don't know. As reference, I am using Rails 2.3.4. I tried modeling my solution from the Ryan's Scraps tutorial on nested models

From what I have tried to debug, it seems that I have two problems but I am not sure why.

  1. When I submit a form with nested models, only part of the nested model information is posted. I only get the first field, not the "n" others the user may have selected
  2. Of the single field that gets posted, there aren't any rows inserted into the join table that I created for the HABTM relationship.

Here is a piece of code and the corresponding logs for my insertion attempt:

Attorney Model:

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

Association Model:

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

Attorneys Controller:

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

Attorney's New View:

<% 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 %>

Membership Partial:

<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', new_association_path %>
<% end %>

Attorney Helper Link:

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

Join Table Migration:

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

Log capture:

    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"=>"[email protected]"}}
  [4;35;1mAttorney Load (15.6ms)[0m   [0mSELECT "attorneys".id FROM "attorneys" WHERE ("attorneys"."email" = '[email protected]') 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, '[email protected]')[0m
Redirected to http://localhost:3000/attorneys/11
Completed in 150ms (DB: 16) | 302 Found [http://localhost/attorneys]

I can see that associations"=>{"association_id"=>"3"} it is only getting the last of the multiple associations that I had for the particular person and it isn't creating any entries into the join table. Where might my code have gone wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

挽梦忆笙歌 2024-08-20 07:03:13

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

这两个问题都源于这部分视图:

<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 的作用。

Accepts_nested_fields_for 用于创建和修改表单中的相关对象。它可用于填充连接表,这正是您想要做的事情。但是,对于 HABTM 关系,使用 Accepts_nested_fields_for 来填充连接表是不可能的。如果您想创建一个与新律师链接的新关联,则可以很好地使用accepts_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 帮助程序,它创建一个链接来克隆会员部分的空实例。 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>

You two have problems here, unfortunately one of them is masked by the other.

Both problems stem from this part of the view:

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

Problem 1: You've misunderstood what accept_nested_fields_for does.

accepts_nested_fields_for is used to create and modify related objects in a form. It can be used to populate join table, which is kind of what you're trying to do. However, using accepts_nested_fields_for to populate the join table is impossible with a HABTM relationship. A good use of accepts_nested_fields_for would be if you wanted to create a new Association that will be linked with the new Attorney. Or if you had a rich join model that required additional information for each record.

Problem 2: You're not linking the fields in this form to the attorney form. Which is necessary to use accepts_nested_fields_for.

We've already established that accepts_nested_fields_for is not what you need to accomplish this, but, you're still not associating the select association_id field with the form. Which is why params[associations][association_id] was set and not params[attorney][associations][association_id].

Problem 3: The form structure is all wrong for what it looks like you're trying to accomplish.

There's a too much that needs correcting for me to give a proper break down. You're better off checking out the complex-forms-example repository. It's a working example of accepts_nested_attributes_for, it doesn't deal with any HABTM relationships, but it should teach you every thing you need to know. The corrected code below is 90 % of what you need. The complex-forms-examples linked above will teach you what you need to know to fill in the blanks that are add_association_link and create_association_link.

The correction involves the following steps:

  1. Create a join model, and change the relationship to a has many through one, accepting nested attributes on the join model.
  2. Make a minor adjustment in the controller, in terms of building things.
  3. Pass the form builder object to the partial.
  4. Rewrite the form in the partial so it is focuses on the newly created join model.

You can accomplish this with the following changes.

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

Attorney Controller:

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

New Attorney View:

<% 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 %>

I'm assuming that add_association_link is a javascript helper that creates a link to clone an empty instance of what was the Membership partial. create_association_link is a place holder for a similar helper that will add a partial for a new association.

Attorney Association Partial:

  <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>

Association Partial:

  <div class="association">
    <%= f.label_for :name %>
    <%= f.text_field :name %>
    <%= link_to_function "remove", "$(this).up('.attorney_association').remove()" %>
  </div>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文