Rails 4:保存子记录ID但不保存其属性,使用嵌套形式与has_many通过关系

Rails 4: child record ID is saved but not its attributes, using nested form with has_many through relationship

我有 3 个模型具有 has_many through 关系:Food(例如:巧克力)、Sub(巧克力食品替代品)、Joint(联合表)。

说@food = Food.find(1); has_many through 关系允许我做 @subs = @food.subs 其中 return 所有与@food 关联的替代品。这工作正常,但是只有 Sub id 被保存而不是它的属性:name 和 :description 正如你所看到的那样 returned nil 当试图在我的创建操作中保存 @food.subs控制器:

=> #<ActiveRecord::Associations::CollectionProxy [#<Sub id: 28,name:nil,description:nil,created_at: 
"2015-01-07 00:40:35", updated_at: "2015-01-07 00:40:35">]>

我想问题出在我的食物控制器中的创建操作上,也可能与我的嵌套表单有关。我花了无数个小时试图弄清楚这一点,我非常渴望找到答案。我真的不知道该去哪里找了。 我是 rails 的新手,非常感谢您的帮助和时间,我真的很感激。如果可能的话,请根据我的初学者水平调整你的答案:-) .

下面是我的控制器样本、表格和相关信息。

这是我的模型:

class Food < ActiveRecord::Base
 has_many :joints
 has_many :subs, :through => :joints
 accepts_nested_attributes_for :subs
end

class Sub < ActiveRecord::Base
 has_many :joints
 has_many :foods, :through => :joints
 accepts_nested_attributes_for :foods
end

class Joint < ActiveRecord::Base
 belongs_to :food
 belongs_to :sub
end

这是我的数据库模式供参考:

create_table "foods", force: true do |t|
 t.string   "name"
 t.text     "description"
 t.datetime "created_at",  null: false
 t.datetime "updated_at",  null: false
end

create_table "joints", force: true do |t|
 t.integer  "food_id"
 t.integer  "sub_id"
 t.datetime "created_at", null: false
 t.datetime "updated_at", null: false
end

create_table "subs", force: true do |t|
 t.string   "name"
 t.text     "description"
 t.datetime "created_at",  null: false
 t.datetime "updated_at",  null: false
end

这是我的 foods_controller:

def new
 @food = Food.new
 @sub = Sub.new
end
def create
 @food = Food.new(food_params)
 @food.subs.build(params[:subs])
 @food.save

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

private

 def food_params
  params.require(:food).permit(:name, :description, subs_attributes: [:name, :description])
 end
end

这是我的 views/foods/_form:

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

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

<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>

<div>
<%= f.fields_for(@sub) do |sub| %>
<div class="field">
  <%= sub.label :name %>
  <%= sub.text_field :name %>
</div>
<div class="field">
  <%= sub.label :description %>
  <%= sub.text_area :description %>
</div>
<% end %>
</div>


<div class="actions">
<%= f.submit %>
</div>
<% end %>

我的路线以防有帮助: 资源:食物

resources :subs

resources :joints

root "foods#index"

非常感谢!

安托万。

在你的新行动中:

def new
  @food = Food.new
  @food.subs.build
end

在您看来:

<%= f.fields_for :subs do |sub| %>

当你直接传递一个对象时,这个对象成为新的 form_builder 的对象 - rails 不知道它以任何方式与原始对象相关联,所以它会导致不同的字段名称。

当您传递一个符号时,rails 将首先尝试查找您当前的对象是否定义了 subs_attributes 方法。如果是这样,它将遍历 subs 关联并为每个关联模型构建字段。

引用here.

更新 - 对评论的回答:

首先 - @subs 不是一个符号,它是一个实例变量。符号以冒号开头,如 :subs。当 fields_for 接收到一个参数时,它会检查它是一个符号还是对象。在前一种情况下,它会搜索与表单生成器 (f.object) 关联的对象,以查明它是否定义了 <passed_symbol>_attributes=。这样它就知道模型接受此关联的嵌套属性,因此它可以相应地运行(为每个关联对象创建新的表单构建器并使用正确的名称 - <symbol>_attributes)。

传递对象时,rails 无法检测它是否以任何方式连接到当前对象 - 您可能对同一类型的对象有两个关联,甚至可能有绝对与原始对象无关。在这种情况下,fields_for 就像嵌套的 form_for - 生成的表单构建器将携带对象的模型名称 (f.object.class.model_name.singular)