rails 4具有嵌套属性和嵌套形式

时间:2014-08-13 16:48:24

标签: ruby-on-rails-4 nested-forms nested-attributes

我正在尝试建模我的第一个rails 4项目,以实现从教程到现实世界的飞跃。

该应用程序是一个马秀注册。

  • 用户(设计)添加一个'条目'。
  • 该条目可以包含一个或多个' rides'
  • 每次骑行都可以拥有一匹马'和一个骑手' (嵌套表单允许用户选择现有的马匹和骑手,或者在新的创建过程中通过表单创建新的马匹。

我开始使用Cocoon和Formtastic做一些嵌套的Web表单: https://github.com/nathanvda/cocoon

购买我在将相关数据保存到表格时遇到问题。

当我保存我的条目时,rides表包含正确的值: test,entry_id,但不是horse_id

条目表没有ride_id值。它只有user_id(我手动设置在控制器中)

我的问题是:
我确信我缺少一些概念性概念,但我是否正确设置了模型关联? 如何编辑当前表单以正确保存数据?

当我在控制器中调试时,我得到的是一个奇怪的参数值:

{" utf8" =>"√"," authenticity_token" =>" xxxxxxxxxxxxxx =",&#34 ;条目" => {" show_date" =>" 10/26/2014"," rides_attributes" => {" 1407930343404& #34; => {"测试" =>"介绍B","马" => {"名称" => ;"普通马"," _destroy" =>""}}}},"提交" =>"创建条目","操作" =>"创建","控制器" =>"条目"}

我以为我会看到' horses_attributes'在' rides_attributes'哈希,但我只看到了马匹#39;

任何澄清,包括与示例的链接都将非常明确。

class User < ActiveRecord::Base

  devise :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable
  has_many :entries 
  has_many :rides, :through => :entries 
  has_many :horses,  :through => :entries   
  has_many :riders, :through => :entries
end

class Entry < ActiveRecord::Base
  belongs_to :user

  has_many :rides, :dependent => :destroy
  has_many :horses, :through => :rides
  has_many :riders, :through => :rides

 accepts_nested_attributes_for :rides, :allow_destroy => true
 accepts_nested_attributes_for :horses 
 accepts_nested_attributes_for :riders
end

class Ride < ActiveRecord::Base
  belongs_to :entry
  belongs_to :user
  has_one :horse #, :dependent => :destroy
  has_one :rider

  accepts_nested_attributes_for :horse
  accepts_nested_attributes_for :rider
end

class Horse < ActiveRecord::Base
  belongs_to :ride # :dependent => :destroy
  belongs_to :user, :dependent => :destroy
end

class Rider < ActiveRecord::Base
    #belongs_to :ride #,  :dependent => :destroy
    belongs_to :user, :dependent => :destroy
    has_many :rides
end

这是我的部分架构:

create_table "entries", force: true do |t|
  t.date     "show_date"
  t.integer  "user_id"
  t.integer  "ride_id"
  t.datetime "created_at"
  t.datetime "updated_at"
end

add_index "entries", ["ride_id"], name: "index_entries_on_ride_id", using: :btree
add_index "entries", ["user_id"], name: "index_entries_on_user_id", using: :btree

create_table "horses", force: true do |t|
  t.string   "name"
  t.string   "horse_ncdcta"
  t.string   "owner_fname"
  t.string   "owner_lname"
  t.date     "coggins_date"
  t.integer  "ride_id"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.integer  "user_id"
end

add_index "horses", ["ride_id"], name: "index_horses_on_ride_id", using: :btree

create_table "riders", force: true do |t|
 t.string   "first_name"
 t.string   "last_name"
 t.string   "rider_ncdcta"
 t.boolean  "senior"
 t.boolean  "liability_signed"
 t.integer  "ride_id"
 t.datetime "created_at"
 t.datetime "updated_at"
end

add_index "riders", ["ride_id"], name: "index_riders_on_ride_id", using: :btree

create_table "rides", force: true do |t|
 t.string   "test"
 t.integer  "entry_id"
 t.integer  "rider_id"
 t.datetime "created_at"
 t.datetime "updated_at"
 t.integer  "horse_id"
end

add_index "rides", ["entry_id"], name: "index_rides_on_entry_id", using: :btree
add_index "rides", ["rider_id"], name: "index_rides_on_rider_id", using: :btree

create_table "users", force: true do |t|
  t.string   "email",                  default: "", null: false
  t.string   "encrypted_password",     default: "", null: false
  t.string   "reset_password_token"
  t.datetime "reset_password_sent_at"
  t.datetime "remember_created_at"
  t.integer  "sign_in_count",          default: 0,  null: false
  t.datetime "current_sign_in_at"
  t.datetime "last_sign_in_at"
  t.string   "current_sign_in_ip"
  t.string   "last_sign_in_ip"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.string   "first_name"
  t.string   "last_name"
  t.string   "phone"
end

部分进入控制器:

def new
  @user=current_user
  @entry=Entry.new
end

def create
  @entry = Entry.new(entry_params)
  @entry.user_id=current_user[:id]

  respond_to do |format|
    if @entry.save  
      # example of params value
      # {"utf8"=>"√", "authenticity_token"=>"xxxxxxxxxxxxxx=", "entry"=>{"show_date"=>"10/26/2014", "rides_attributes"=>{"1407930343404"=>{"test"=>"Intro B", "horses"=>{"name"=>"Plain Horse", "_destroy"=>""}}}}, "commit"=>"Create Entry", "action"=>"create", "controller"=>"entries"}
      debugger
      format.html { redirect_to @entry, notice: 'Entry was successfully created.' }
      format.json { render :show, status: :created, location: @entry }
  else
    format.html { render :new }
    format.json { render json: @entry.errors, status: :unprocessable_entity }
  end
 end
end

def entry_params
  # added ids based on comments below
  params.require(:entry).permit(:show_date, rides_attributes: [:id, :test,  :_destroy, horses_attributes: [:name, :id]] ) 
end

的观点:
_form.html.haml

= semantic_form_for @entry do |f|
  = f.inputs do
  = f.label :show_date, "Show Date"
  = f.input :show_date, :as => :select, :collection => [['08/03/2014', '08/03/2014'], ['09/14/2014', '09/14/2014'], ['10/26/2014', '10/26/2014'], ['11/15/2014', '11/15/2014']]

  %h3 Rides
  #rides
    = f.semantic_fields_for :rides do |ride|
      = render 'ride_fields', :f => ride

    .links
      = link_to_add_association 'add ride', f, :rides

  = f.actions do
    = f.action :submit

_ride_fields.html.haml

.nested-fields
  = f.inputs do
  = f.label :test, "Test"
  = f.input :test, :as => :select, :collection => [['Intro A', 'Intro A'], ['Intro B', 'Intro B'], ['Intro C', 'Intro C']]

  = f.semantic_fields_for :horses do |horse|
    = render 'horse_fields', :f => horse

    = link_to_add_association 'add horse', f, :horse

_horse_fields.html.haml

.nested-fields
  =f.inputs do
  = f.input :name
.links
  = link_to_add_association 'add horse', f, :horse

3 个答案:

答案 0 :(得分:0)

这就是我认为正在发生的事情,你是在Rides而不是Entry中嵌套。 Ride接受has_one马关系的嵌套属性,因此您可以看到“马”。如果你提交了针对Entry的马(它有has_many关系),你会看到horses_attributes。

在_form.html.haml的这一部分中,您已将ride分配给f

 = render 'ride_fields', :f => ride

然后在你的_ride_fields.html.haml中使用f创建一个嵌套表单,该表单是从ride变量(Ride has_one horse)创建的。

 = f.semantic_fields_for :horses do |horse|
   = render 'horse_fields', :f => horse

由于您的Ride模型已accepts_nested_attributes_for :horse,因此您在骑行中看到horses。要查看horses_attributes,您需要针对条目创建匹配马(它接受“马匹”的嵌套属性)。

我相信您可以通过更改此render来修复此问题(在条目模型表单中传递):

= render 'ride_fields', :f => ride, :e => f

然后马嵌套这样的形式(从fe):

 = e.semantic_fields_for :horses do |horse|
   = render 'horse_fields', :f => horse

答案 1 :(得分:0)

使用嵌套属性时,传递给form_for的符号必须与关联名称匹配。你在做什么

f.semantic_fields_for :horses

但是游乐设施没有马匹协会,只有马协会。因为这个rails没有意识到你正在尝试使用嵌套属性,这就是为什么数据嵌套在键“horses”而不是“horses_attributes”之下。您当然需要更改允许的参数以匹配。

此外,如果骑行实际上只有一匹马,那么你不需要所有的东西来管理为骑行添加额外的马场。如果这是一个错误,那么请单独保留表单,但更改模型以便乘坐has_many :horsesaccepts_nested_attributes_for :horses

答案 2 :(得分:0)

我现在正确地使用下面的代码保存关系(有一个警告)。感谢以前的海报让我朝着正确的方向前进!

还有Formtastic文档链接:http://rdoc.info/github/justinfrench/formtastic/Formtastic/Inputs/SelectInput

警告:如果我不想添加新马(_ride_fields.html.haml),我无法弄清楚如何选择具有表单选择的现有马。添加新马工作正常 当我尝试使用当前注释的代码时,我得到错误:
骑行的未定义方法ride_id 我不确定这是一个形式问题,模型相关还是控制器。如果我不明白,我会将此作为一个单独的问题发布。

我在原帖中意识到,我不应该将外键(例如ride_id)保存到&#39;条目&#39; table因为它们被保存到entry_id字段中的相关表(rides)中。

条目控制器(部分):

class EntriesController < ApplicationController  
  before_filter :set_horses, :except => [:destroy, :index]

def new
  @c_user=current_user[:id]
  @user=current_user
  @entry = current_user.entries.new
  @horses = current_user.horses
end

def create

  @entry = current_user.entries.new(entry_params)

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

private
# Use callbacks to share common setup or constraints between actions.

def set_horses
 if current_user.admin?
   @horses=Horse.all
 else
   @horses = current_user.horses 
 end 
end

# Never trust parameters from the scary internet, only allow the white list through.
def entry_params
  params.require(:entry).permit(:show_date, rides_attributes: [:id, :test,  :_destroy, horse_attributes: [:name, :id]] ) 
  end
end

entry.rb:

class Entry < ActiveRecord::Base
  belongs_to :user
  has_many :rides, :dependent => :destroy
  has_many :horses, :through => :rides, :dependent => :destroy
  has_many :riders, :through => :rides, :dependent => :destroy

  accepts_nested_attributes_for :rides, :reject_if => lambda { |a| a[:test].blank? }, :allow_destroy => true
  accepts_nested_attributes_for :horses, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
  accepts_nested_attributes_for :riders, :allow_destroy => true

  validates_presence_of :show_date
end

ride.rb:

class Ride < ActiveRecord::Base
  belongs_to :entry, inverse_of: :entry
  belongs_to :user, inverse_of: :user
  has_one :horse 
  has_one :rider

  accepts_nested_attributes_for :horse
end

horse.rb:

class Horse < ActiveRecord::Base
  belongs_to :ride, :dependent => :destroy
  belongs_to :user, :dependent => :destroy
end

_form.html.haml

= semantic_form_for @entry do |f|
  = f.inputs do
  = f.input :show_date, :as => :select, :collection => ['2014/08/03', '2014/09/14', '2014/10/26', '2014/11/15']
  %h3 Rides
  #rides
    = f.semantic_fields_for :rides do |ride|
      = render 'ride_fields', :f => ride 
    .links
      = link_to_add_association 'add ride', f, :rides

  = f.actions do
    = f.action :submit

_ride_fields.html.haml

.nested-fields
  = f.inputs do
    = f.label :test, "Test"
   = f.input :test, :as => :select, :collection => [['Intro A', 'Intro A'], ['Intro B', 'Intro B'], ['Intro C', 'Intro C']]

    = if !@horses.empty?
      -# both of next 2 lines give error: undefined method ride_id for <Ride:0x00000008cc3370>  
      -#= f.input :horse, :as => :select, :collection => @horses
      -#= f.input :horse, :as => :select, :member_label => :name

    = f.semantic_fields_for :horse do |horse|
      = render 'horse_fields', :f => horse

  .links
    = link_to_add_association 'add new horse', f, :horse

_horse_fields.html.haml

.nested-fields
  = f.inputs do
    = f.input :name