使用has_many:through的不属于当前对象的嵌套属性

时间:2013-12-31 15:40:44

标签: ruby ruby-on-rails-4 associations nested-attributes has-many-through

我的最终目标是能够在协议视图中将协议添加到协议中,而不管它们是否存在于Costume数据库中。我的主要困难是服装不属于协议,它们独立存在但可以添加到协议中。如果添加了不在Costume数据库中的新服装,它将把它添加到数据库中。有没有办法做到这一点?我无法在任何地方找到关于此的教程。如果我可以从this发布控制器,我认为这就是我所需要的。我只需要在每次显示表单时创建一个服装。非常感谢。

我的模型如下:

# app/models/agreement.rb
class Agreement < ActiveRecord::Base
  has_and_belongs_to_many :costumes, join_table: :agreement_costumes
  accepts_nested_attributes_for :costumes, :reject_if => :all_blank
end

# app/models/costume.rb
class Costume < ActiveRecord::Base
  has_and_belongs_to_many :agreements, join_table: :agreement_costumes
end

# app/models/agreement_costume.rb
class AgreementCostume < ActiveRecord::Base
  belongs_to :agreement
  belongs_to :costume
end

我的协议控制器如下:

class AgreementsController < ApplicationController
  before_action :set_agreement, only: [:show, :edit, :update, :destroy]

  # Some methods ommitted

  # GET /agreements/new
  def new
    @agreement = Agreement.new
    @agreement.costumes.build
    @costumes = Costume.all
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_agreement
      @agreement = Agreement.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def agreement_params
      params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid, :title, :start, :end, :due, :financer, :employee, :costumes_attributes => [:cid, :description, :wd, :back, :photo])
    end
:end

最后,协议视图

<%= form_for(@agreement) do |f| %>
  <% if @agreement.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@agreement.errors.count, "error") %> prohibited this agreement from being saved:</h2>

      <ul>
      <% @agreement.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <!-- Agreement fields omitted -->

  <%= f.fields_for :costumes do |c| %>
    <div class="field">
      <%= c.label :cid %><br>
      <%= c.number_field :cid %>
    </div>
    <div class="field">
      <%= c.label :description %><br>
      <%= c.text_field :description %>
    </div>
    <!-- etc with costume fields -->
  <% end %>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

1 个答案:

答案 0 :(得分:1)

如果您想在那里创建新服装,则需要在协议控制器中accepts_nested_attributes_for

class Agreement < ActiveRecord::Base
  has_many :agreement_costumes
  has_many :costumes, through: :agreement_costumes

  accepts_nested_attributes_for :costumes, :reject_if => :all_blank, :allow_destroy => :false,
end

然后在您的协议控制器中的agreements#new操作中构建服装条目

def new
  @agreement = Agreement.new
  @agreement.costumes.build
  @costumes = Costumes.all
end

@agreement.costumes.build创建与此协议相关的服装空白实例。然后使用:costumes在表单中访问该服装的参数。不要忘记在协议控制器中将嵌套的参数列入白名单:

def agreement_params
  params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid,
   :title, :start, :end, :due, :financer, :employee, costumes_attributes[:name, :price, :id])
end

现在,您的表单必须有一个位置,可以从列表中选择现有服装和/或添加新服装。

<%= f.label :costumes, "Costumes" %>
<%= f.collection_select :costumes, :agreement_id, :id, :name, price, {}, {multiple: true} %>

<strong>Add a new costume</strong>
<%= f.fields_for :costumes do |c|
  <%= c.label :name %>
  <%= c.text_field :name %>
    <br>
  <%= c.label :price %>
  <%= c.number_field :price %>
    <br>
<% end %>

这应该可以帮助你完成大部分工作。我不得不猜测你的一些代码,所以这不是一个剪切和粘贴的答案。你需要建立你可以做到的东西,并可能在这里和那里做更多的谷歌搜索。如果你想有一个弹出窗体可以动态地将服装添加到列表中然后能够在collection_select中选择它,你必须在应用程序中打开Turbo_links,创建一个Javascript弹出窗体。然后使用AJAX提交表单,将服装保存到数据库,运行另一个.js.erb文件,然后通过您的Javascript重新加载该列表来更新collection_select文本列表。实际上,这可能比以这种形式嵌套的新服装形式更容易。

相关问题