Как сохранить мои дочерние записи из родительского контроллера?

У меня уже есть куча объектов "kid", и я хочу создать родительский объект, который связан с детьми через "относительную" модель.

Этот объект дает мне много-ко-многим, через родственников.

Чтобы быть ясным: пользователь посещает страницу "родители", нажимает на создание родителей и получает форму, которая позволяет им называть родителя и содержать до четырех детей этому родителю (путем создания "родственников" ), каждый из которых эти "отношения" также названы - это важная часть. Итак, я мог бы назвать отношение "студент-сын" или "сын", например.

Вот код, который у меня есть до сих пор:

class Kid < ActiveRecord::Base
  has_many :relatives
  has_many :parents, through: :relatives
end


class Parent < ActiveRecord::Base
  has_many :relatives
  has_many :kids, through: :relatives

  accepts_nested_attributes_for :relatives,
    :reject_if => lambda { |a| a[:content].blank? },
    :allow_destroy => true
end


class Relative < ActiveRecord::Base
  belongs_to :parent
  belongs_to :kid
end



class ParentsController < ApplicationController
  before_action :set_parent, only: [:show, :edit, :update, :destroy]
  before_action :lookup_kids, only: [:new, :edit]

  # GET /parents
  # GET /parents.json
  def index
    @parents = Parent.all
  end

  # GET /parents/1
  # GET /parents/1.json
  def show
  end

  # GET /parents/new
  def new    
    @parent = Parent.new
    4.times { @parent.relatives.build }
  end

  # GET /parents/1/edit
  def edit
  end

  # POST /parents
  # POST /parents.json
  def create
    @parent = Parent.new(parent_params)        

    parent_params[:relatives_attributes].each do |k,r|
      @parent.relatives.build(r.except(:_destroy))
    end

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

  # cut for brevity.



  private

    # Use callbacks to share common setup or constraints between actions.
    def set_parent
      @parent = Parent.find(params[:id])
    end


    def parent_params
      params.require(:parent).permit(:name,
       relatives_attributes: [:parent_id, :kid_id, :relationship, :_destroy])
    end
    def lookup_kids
      @kids = Kid.all #for this nursery.
    end
end





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

      <% @parent.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>

  <h4>Kids:</h4>
  <%= f.fields_for :relatives do |r| %>
    <%= r.label :kid %>
    <%= r.collection_select :kid_id,
      @kids, :id, :name, include_blank: true%>
    <%= r.label :relationship %>
    <%= r.text_field :relationship %>    
    <%= r.check_box :_destroy %>
    <%= r.label :_destroy, "Remove" %>
    <br/>
  <% end %>

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



ActiveRecord::Schema.define(version: 20151030113634) do
  create_table "kids", force: :cascade do |t|
    t.string   "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end
  create_table "parents", force: :cascade do |t|
    t.string   "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end
  create_table "relatives", force: :cascade do |t|
    t.string   "relationship"
    t.integer  "parent_id"
    t.integer  "kid_id"
    t.datetime "created_at",   null: false
    t.datetime "updated_at",   null: false
  end
  add_index "relatives", ["kid_id"], name: "index_relatives_on_kid_id"
  add_index "relatives", ["parent_id"], name: "index_relatives_on_parent_id"
end

Когда я получаю "создать" в контроллере родителей, я вижу, что правильные параметры проходят, но записи отношений не сохраняются. Не может ли это произойти автоматически?

Я пробовал зацикливаться на: attributes_attributes, но, похоже, не работает с 'build'.

Как я могу заставить записи "родственников" сохранить?

EDIT: добавлены параметры:

parent"=>{
  "name"=>"Dad",
  "relatives_attributes"=>{
     "0"=>{"kid_id"=>"2", "relationship"=>"Son", "_destroy"=>"0"},
     "1"=>{"kid_id"=>"", "relationship"=>"", "_destroy"=>"0"},
     "2"=>{"kid_id"=>"", "relationship"=>"", "_destroy"=>"0"},
     "3"=>{"kid_id"=>"", "relationship"=>"", "_destroy"=>"0"}}}

Изменить: я обновил это, чтобы показать свое последнее изменение - обратите внимание на родительские_парки [: родственники_атрибуты].each do | k, r | ' в контроллере. Теперь это сохраняет записи малыша, но единственная проблема заключается в том, что он также сохраняет поля, которые пусты! Поэтому у меня есть "относительные" записи с нулевыми значениями для записей малыша. Как я могу остановить его, сохраняя пустые поля (или создавая пустые относительные записи)?

Ответ 1

Ответ заключался в том, чтобы построить каждую вспомогательную запись относительно, например:

parent_params[:relatives_attributes].each do |k,r|
  @parent.relatives.build(r.except(:_destroy))
end

Перед вызовом @parent.save.

Однако у меня все еще есть проблемы, избавляющиеся от пустых записей. Поэтому, если у кого-то есть ответ на эту проблему, прокомментируйте здесь - или если есть лучший или более традиционный способ сделать это, ударил меня. Следующий вопрос здесь: Почему этот reject_if в моей модели не отклоняет пустые записи?

Ответ 2

Вы почти там, в зависимости от того, как выглядит ваша форма, вам, скорее всего, понадобится accepts_nested_attribute_for в вашем Относительном ассоциативном классе:

class Relative
  belongs_to :parent
  accepts_nested_attributes_for :parent
  belongs_to :kid 
  accepts_nested_attributes_for :kid
end

Если это не сработает, отправьте свои параметры, которые передаются в контроллер, и мы можем соответствующим образом настроить