如何正确设置此嵌套关联?

时间:2012-07-29 17:59:07

标签: ruby-on-rails database nested-attributes

我有以下型号:

class MealPlan < ActiveRecord::Base
  has_many :food_contents
  has_many :foods,:through => :food_contents
  accepts_nested_attributes_for :food_contents
  attr_accessible :food_contents_attributes,:name
end

class Food < ActiveRecord::Base
  validates_presence_of :name,:protein,:carbs,:fats,:calories
  validates_numericality_of :protein,:carbs,:fats,:calories
end

class FoodContent < ActiveRecord::Base
  belongs_to :meal_plan
  belongs_to :food

  attr_accessible :food_id, :how_much,:meal_plan_id
  validates_presence_of :food,:meal_plan
end

我在膳食计划控制器中有以下代码:

  def new
    @meal_plan = MealPlan.new
    3.times { @meal_plan.food_contents.build }
  end


def create
  @meal_plan = MealPlan.new(params[:meal_plan])
end

以及用餐计划的以下表格:

<%= form_for(@meal_plan) do |f| %>
  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>
  <%= f.fields_for :food_contents do |b| %>
    <fieldset>
      <legend>New food</legend>
      <%= b.collection_select :food_id,Food.all,:id,:name,{},{:class => "food_id_selector"} %><br/>
      <%= b.text_field :how_much,:class => "how_much_input" %><br/>
      <%= content_tag(:p,nil,:class => "food_acumulator") %>
    </fieldset>
  <% end %>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

但是,当它始终无法保存模型时,会出现错误:

> #<ActiveModel::Errors:0xb34212e8
 @base=#<MealPlan id: nil, name: "Sample", created_at: nil, updated_at: nil>,
 @messages={:"food_contents.meal_plan"=>["can't be blank"]}>

根据我的调试,罪魁祸首是来自validates_presence_of :meal_plan

class FoodContent < ActiveRecord::Base
  ...
  ...
  validates_presence_of :food,:meal_plan
end

一方面,我可以理解为什么它不能保存嵌套模型(因为用餐计划还没有id),但另一方面,我想确保我所做的是正确的。

2 个答案:

答案 0 :(得分:3)

我已经摆弄了一下,找到了一种方法可以通过跳过meal_plan上的验证来保存你的三个模型:

mp = MealPlan.new
fc = mp.food_contents.build
f = fc.build_food
f.save
mp.save(validate: false)
fc.save

这应该不是问题,因为在保存fc时会再次验证,您可以通过以下方式验证:

mp = MealPlan.new
fc = mp.food_contents.build
f = fc.build_food
f.save
mp.save(validate: false)
fc.food = nil
fc.save

答案 1 :(得分:0)

我知道这已经得到了解答,但更简单的方法是在没有验证的情况下进行保存,只需设置id = 1.这样,id的验证通过以及将记录写入数据库时,id将被正确的覆盖。

所以你可以简单地做一些事情:

@mp = MealPlan.new(your_strong_param_access_function) # all ur nested stuff should then be automatically pulled into mp including your associations.
@mp.food_contents.each do |fc|
  fc.meal_plan_id = 1
end
@mp.save

就是这样。验证不会失败,因为meal_plan_id设置为1。 当活动记录保存所有内容时,它会将meal_plan_id更新为正确的值。 所有你的其他验证仍在积极工作:)