【问题标题】:Nested forms with three models through another one?通过另一个模型嵌套三个模型?
【发布时间】:2013-12-11 17:28:50
【问题描述】:

我正在尝试创建一个嵌套表单,其中包含三个关联的模型。

这是架构:

这些是模型:

../app/models/alimento.rb:

class Alimento < ActiveRecord::Base

  attr_accessible :calorias, :nome, :refeicaos_attributes

  validates :nome, :calorias, :presence => { :message => "nao pode ficar em branco" }
    has_many :controles, :dependent => :destroy
    has_many :refeicaos, through: :controles
    has_many :diarios, through: :controles

    accepts_nested_attributes_for :controles
end

../app/models/refeicao.rb:

class Refeicao < ActiveRecord::Base

  attr_accessible :nome, :alimentos_attributes, :controles_attributes, :diario_attributes

    validates :nome, :presence => { :message => "nao pode ficar em branco" }

    has_many :controles, dependent: :destroy
    has_many :alimentos, through: :controles
    has_many :diarios, through: :controles
    accepts_nested_attributes_for :controles
end

../app/models/diario.rb:

class Diario < ActiveRecord::Base

    has_many :controles, dependent: :destroy
    has_many :refeicaos, through: :controles
    has_many :alimentos, through: :controles
    accepts_nested_attributes_for :controles

  attr_accessible :data, :controles_attributes, :refeicaos_attributes, :alimentos_attributes
end

../app/models/controle.rb:

class Controle < ActiveRecord::Base

  belongs_to :alimento 
  belongs_to :refeicao
  belongs_to :diario

  attr_accessible :quantidade, :alimento_id, :refeicao_id, :diario_id

    accepts_nested_attributes_for :alimento
    accepts_nested_attributes_for :refeicao
    accepts_nested_attributes_for :diario

end

我创建了许多alimentos(食物)和refeições(膳食),现在我需要创建一个
通过Controle(控制)模型可以包含许多alimentosrefeições的模型Diario。

../app/controllers/diarios_controller.rb:

class DiariosController < ApplicationController
  # GET /diarios
  # GET /diarios.json
  def index
    @diarios = Diario.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @diarios }
    end
  end

  # GET /diarios/1
  # GET /diarios/1.json
  def show
    @diario = Diario.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @diario }
    end
  end

  # GET /diarios/new
  # GET /diarios/new.json
  def new
    @diario = Diario.new
  end

  # GET /diarios/1/edit
  def edit
    @diario = Diario.find(params[:id])
  end

  # POST /diarios
  # POST /diarios.json
  def create
    @diario = Diario.new(params[:diario])

    respond_to do |format|
      if @diario.save
        format.html { redirect_to @diario, notice: 'Diario was successfully created.' }
        format.json { render json: @diario, status: :created, location: @diario }
      else
        format.html { render action: "new" }
        format.json { render json: @diario.errors, status: :unprocessable_entity }
      end
    end
  end

  # PUT /diarios/1
  # PUT /diarios/1.json
  def update
    @diario = Diario.find(params[:id])

    respond_to do |format|
      if @diario.update_attributes(params[:diario])
        format.html { redirect_to @diario, notice: 'Diario was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @diario.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /diarios/1
  # DELETE /diarios/1.json
  def destroy
    @diario = Diario.find(params[:id])
    @diario.destroy

    respond_to do |format|
      format.html { redirect_to diarios_url }
      format.json { head :no_content }
    end
  end
end

我正在尝试做“Nested Model Form (revised)”的例子。

观看次数:

../app/views/diarios/new.html.erb:

<%= form_for @diario do |f| %>
  <div class="field">
    <%= f.label :data %>
    <%= f.date_select :data %>
    <br>
  </div>
  <%= f.fields_for :refeicaos do |builder| %>
    <%= render 'refeicao_fields', f: builder %>      
  <% end %>
  <%= link_to_add_fields "Adicionar Refeição", f, :refeicaos %>
</br>
</br>
  <div class="actions">
    <%= f.submit "Cadastrar Controle" %>
  </div>
<% end %>
</br>
<%= link_to 'Voltar', root_path %>

../app/views/diarios/_refeicao_fields.html.erb:

<fieldset>
  <strong>Refeição: </strong></br>
  <%= f.label :nome, "Nome da Refeição", :style => 'margin-left: 5px;' %>
  <%= collection_select(:refeicao, :id, Refeicao.order(:nome), :id, :nome) %>
  <%= f.check_box :_destroy %>
  <%= f.label :_destroy, "Remover Refeição" %>
</br>
</br>
  <strong>Alimentos:</strong></br>
  <%= f.fields_for :alimentos do |builder| %>
    <%= render 'alimento_fields', f: builder %>
  <% end %>
  <%= link_to_add_fields "Adicionar Refeição", f, :alimentos %>
</fieldset>

../app/views/diarios/_alimento_fields.html.erb:

<fieldset>
  <%= f.label :alimento, "Nome do Alimento:" %>
  <%= collection_select(:alimento, :id, Alimento.order(:nome), :id, :nome) %>
  <%= f.hidden_field :_destroy %>
    <%= f.fields_for :controles do |builder| %>
    <%= render 'controle_fields', f: builder %>
  <% end %>
  <%= link_to "Remover alimento", '#', class: "remove_fields" %></br>

</fieldset>

../app/views/diarios/_controle_fields.html.erb:

<fieldset>
  <%= f.label :alimento, "Quantidade:", :style => 'margin-left: 42px;' %>
  <%= f.number_field :quantidade, :style => 'width: 50px;' %>
</fieldset>

已创建自定义助手:

module ApplicationHelper
  def link_to_add_fields(name, f, association)
    new_object = f.object.send(association).klass.new
    id = new_object.object_id
    fields = f.fields_for(association, new_object, child_index: id) do |builder|
      render(association.to_s.singularize + "_fields", f: builder)
    end
    link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
  end
end

我收到此错误:

NoMethodError in Diarios#new

Showing D:/aplicacoes_indie/AppDieta/app/views/diarios/_refeicao_fields.html.erb where line #13 raised:

undefined method `alimentos' for nil:NilClass
Extracted source (around line #13):

10:   <%= f.fields_for :alimentos do |builder| %>
11:     <%= render 'alimento_fields', f: builder %>
12:   <% end %>
13:   <%= link_to_add_fields "Adicionar Refeição", f, :alimentos %>
14: </fieldset>

我尝试了很多方法,但我无法解决这个问题。想法是:日记 > 有 1 个或多个 refeicoes > 有 1 个或多个 alimentos 并通过控制模型保存每个关联。

我哪里错了?

已编辑*

感谢回答,现在表格出现了

但应该在从 alimento 中选择下方出现来自 controle 的数字字段“quantidade”,如下所示:

http://uploaddeimagens.com.br/images/000/123/917/full/model2.jpg?1385480010

../app/view/diarios/_alimento_fields.html.erb

<fieldset>
  <%= f.label :alimento, "Nome do Alimento:" %>
  <%= collection_select(:alimento, :id, Alimento.order(:nome), :id, :nome) %>
  <%= f.hidden_field :_destroy %>
  <%= f.fields_for :controles do |builder| %>
    <%= render 'controle_fields', f: builder %>
  <% end %>
  <%= link_to "Remover alimento", '#', class: "remove_fields" %></br>

</fieldset>

../app/view/diarios/_controle_fields.html.erb

<fieldset>
  <%= f.label :controle, "Quantidade:", :style => 'margin-left: 42px;' %>
  <%= f.number_field :quantidade, :style => 'width: 50px;' %>
</fieldset>

还有一件事,我如何安装 Diario 的创建操作来获取 alimentos 和 refeicaos 选择的所有值并将每个值保存在 controle 模型中?因为我保存并只创建了一个没有关联的 Diario 对象。

../app/controllers/diarios_controller.rb 动作创建

  def create
    @diario = Diario.new(params[:diario])

    respond_to do |format|
      if @diario.save
        format.html { redirect_to @diario, notice: 'Diario was successfully created.' }
        format.json { render json: @diario, status: :created, location: @diario }
      else
        format.html { render action: "new" }
        format.json { render json: @diario.errors, status: :unprocessable_entity }
      end
    end
  end

【问题讨论】:

  • 我相信您会收到此错误,因为 Diario 只能接受 :controles 而不是 :refeicaos:alimentos 的嵌套属性

标签: ruby-on-rails ruby ruby-on-rails-3 forms model


【解决方案1】:

我认为这些是您应该需要的唯一 accepts_nested_attributes_for 语句:

class Diario < ActiveRecord::Base
accepts_nested_attributes_for :refeicaos

还有:

class Refeicao < ActiveRecord::Base
accepts_nested_attributes_for :alimentos

现在&lt;%= f.fields_for :refeicaos do |builder| %&gt; 语句正在设置builderbuilder.object 为零。

编辑 - 将字段名称更改为复数形式,因为这是一个 has_many 关系。

【讨论】:

  • 感谢您的回答,现在表格出现了,但应该出现来自控制的数字字段“quantidade”,在从 alimento 中选择下方,我用图像编辑了问题。
  • 我错过了你有一个来自Controle的字段。您需要将 accepts_nested_attributes_for :controles 语句保留在 Alimento 模型中。
  • 是的,accepts_nested_attributes_for :controles 已经存在于 Alimento 中,但没有出现字段 quantidade。
  • 如果diario 已经保存并且存在适当的controles 表行来连接所有三个记录,则此表单布局应该可以工作。但我不确定在new 操作内部构建初始diario 时是否有办法使这项工作。在这种情况下,Rails 似乎总是会尝试将两行保存到controles 表中——一行用于diario-refeicao 链接,另一行用于refeicao-alimento 链接。您可能需要将其拆分为不同的视图,而不是一次全部编辑。
  • 但是我无法保存日记,然后在同一操作中保存控件?
【解决方案2】:

尝试:

class Alimento < ActiveRecord::Base

  attr_accessible :calorias, :nome, :refeicaos_attributes

  validates :nome, :calorias, :presence => { :message => "nao pode ficar em branco" }
    has_many :controles, :dependent => :destroy
    has_many :refeicaos, through: :controles
    has_many :diarios, through: :controles

    accepts_nested_attributes_for :controles
end

class Refeicao < ActiveRecord::Base

  attr_accessible :nome, :alimentos_attributes, :controles_attributes, :diario_attributes

    validates :nome, :presence => { :message => "nao pode ficar em branco" }

    has_many :controles, dependent: :destroy
    has_many :alimentos, through: :controles
    has_many :diarios, through: :controles
    accepts_nested_attributes_for :alimentos
end

class Diario < ActiveRecord::Base

    has_many :controles, dependent: :destroy
    has_many :refeicaos, through: :controles
    has_many :alimentos, through: :controles
    accepts_nested_attributes_for :refaicoas

  attr_accessible :data, :controles_attributes, :refeicaos_attributes, :alimentos_attributes
end

class Controle < ActiveRecord::Base

  belongs_to :alimento 
  belongs_to :refeicao
  belongs_to :diario

  attr_accessible :quantidade, :alimento_id, :refeicao_id, :diario_id

end

【讨论】:

  • 我试图这样做并得到这个错误:NoMethodError in Diarios#newShowing D:/aplicacoes_indie/AppDieta/app/views/diarios/new.html.erb where line #1 raised:undefined method 'model_name' for NilClass:Class Extracted source (around line #1):1: &lt;%= form_for @diario do |f| %&gt; 2: &lt;div class="field"&gt; 3: &lt;%= f.label :data %&gt; 4: &lt;%= f.date_select :data %&gt;
  • 您是否对new 操作进行了任何更改?如果没有,请改用&lt;%= form_for Diario.new do |f| %&gt;
  • 我解决了这个问题,只是在 accept_nested_attributes_for 的名称中添加一个 's',例如:Class Refeicao accept_nested_attributes_for :alimentos 而不是 accept_nested_attributes_for :alimento
猜你喜欢
  • 1970-01-01
  • 2017-02-01
  • 1970-01-01
  • 2014-04-02
  • 1970-01-01
  • 2015-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多