【问题标题】:Creating Rails 4 Form with Join Table Metadata使用连接表元数据创建 Rails 4 表单
【发布时间】:2014-10-06 07:13:57
【问题描述】:

这里是非常新的 Rails 4 开发人员。我有一个用户正在创建练习的表单。练习可以有很多设备,设备可以是可选的(认为俯卧撑代表做俯卧撑)。我将这个“可选”字段存储在连接表 exercise_equipment 中。

我无法通过我选择的集合元素的值实际发送参数。模型、视图、控制器和参数见下文。

以下是我的模型的属性/关系:

# id        :integer
# name      :string
# is_public :boolean
Exercise
    has_many :exercise_equipment
    has_many :equipment, :through => :exercise_equipment
    accepts_nested_attributes_for :exercise_equipment


# id           :integer
# exercise_id  :integer
# equipment_id :integer
# optional     :boolean
ExerciseEquipment
    belongs_to :exercise
    belongs_to :equipment
    accepts_nested_attributes_for :equipment


# id   :integer
# name :string
Equipment
    has_many :exercise_equipment
    has_many :exercises, :through => :exercise_equipment

这里有一些(可能)相关的控制器方法:

def new
  @exercise = Exercise.new
  @exercise.exercise_equipment.build
end

def create
  @exercise = Exercise.new( exercise_params )
  if @exercise.save
    redirect_to @exercises
  else
    render 'new'
  end
end

def edit
  @exercise = Exercise.find( params[:id] )
end

def update
  @exercise = Exercise.find( params[:id] )
  if @exercise.update_attributes( exercise_params )
    redirect_to @exercises
  else
    render 'edit'
  end
end

def exercise_params
  params.require( :exercise ).permit(
    :name,
    :is_public,
    exercise_equipment_attributes: [
      :id,
      :optional,
      equipment_attributes: [
        :id,
        :name
      ],
    ]
  )
end

这是我创建一个视图来做我想做的事:

练习/new.html.erb

<%= form_for @exercise do |f| %>
  <%= render 'form', f: f %>
  <%= f.submit "New Exercise" %>
<% end %>

练习/_form.html.erb

<%= f.label :name %><br />
<%= f.text_field :name %>

<%= f.check_box :is_public %> Public

<%= f.fields_for( :exercise_equipment ) do |eef|
  <%= eef.fields_for( :equipment ) do |ef|
    ef.collection_select :id, Equipment.all, :id, :name %>
  <% end %>
  <%= eef.check_box :is_optional %> Optional
<% end %>

当我将所有这些放在一起并提交对一个已经存在的练习的更新时,这些值都经过了 params 哈希,但不会更改为我选择的新值...

Parameters: {
  "utf8"=>"[checkbox]",
  "authenticity_token"=>"[token]",
  "exercise"=>{
    "name"=>"Test", 
    "is_public"=>"1", 
    "exercise_equipment_attributes"=>{
      "0"=>{
        "equipment_attributes"=>{
          "id"=>"1"
        }, 
        "optional"=>"1", 
        "id"=>"2" 
      } 
    } 
  },
  "commit"=>"Save Exercise",
  "id"=>"1" 
}

如果你能帮助我,我会非常感激。如果您需要更多信息,请告诉我,我可以提供。

编辑

这是更新前数据库的状态:

postgres@=>db=# select id, name, is_public from exercises;
 id | name | is_public 
----+------+-----------
  2 | Test | t
(1 row)

Time: 61.279 ms
postgres@=>db=# select id, exercise_id, equipment_id, optional from exercise_equipment;
 id | exercise_id | equipment_id | optional 
----+-------------+--------------+----------
  2 |           2 |            1 | t
(1 row)

Time: 58.819 ms
postgres@=>db=# select id, name from equipment where id = 1;
 id |    name     
----+-------------
  1 | Freeweights
(1 row)

然后我转到该练习的更新路线,从集合中选择不同的设备,然后提交表格。我得到以下 Rails 控制台结果:

Started PATCH "/exercises/system-test" for 127.0.0.1 at 2014-08-12 23:48:18 -0400
Processing by ExercisesController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"PsbbUPSCiIew2Fd22Swn+K4PmLjwNDCrDdwXf9YBcm8=", "exercise"=>{"name"=>"Test", "is_public"=>"1", "exercise_equipment_attributes"=>{"0"=>{"equipment_attributes"=>{"id"=>"1"}, "optional"=>"1", "id"=>"2"}}}, "commit"=>"Save Exercise", "id"=>"system-test"}
  Exercise Load (60.5ms)  SELECT  "exercises".* FROM "exercises"  WHERE "exercises"."slug" = 'system-test'  ORDER BY "exercises"."id" ASC LIMIT 1
   (57.3ms)  BEGIN
  ExerciseEquipment Load (76.2ms)  SELECT "exercise_equipment".* FROM "exercise_equipment"  WHERE "exercise_equipment"."exercise_id" = $1 AND "exercise_equipment"."id" IN (2)  [["exercise_id", 2]]
  Equipment Load (59.1ms)  SELECT  "equipment".* FROM "equipment"  WHERE "equipment"."id" = $1 LIMIT 1  [["id", 1]]
  User Load (60.3ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = $1 LIMIT 1  [["id", 10]]
  Exercise Exists (60.5ms)  SELECT  1 AS one FROM "exercises"  WHERE ("exercises"."name" = 'Test' AND "exercises"."id" != 2 AND "exercises"."user_id" = 10) LIMIT 1
   (64.8ms)  COMMIT
Redirected to http://localhost:3000/exercises/system-test
Completed 302 Found in 590ms (ActiveRecord: 580.0ms)


Started GET "/exercises/system-test" for 127.0.0.1 at 2014-08-12 23:48:19 -0400
Processing by ExercisesController#show as HTML
  Parameters: {"id"=>"system-test"}
  Exercise Load (64.1ms)  SELECT  "exercises".* FROM "exercises"  WHERE "exercises"."slug" = 'system-test'  ORDER BY "exercises"."id" ASC LIMIT 1
  Equipment Load (58.7ms)  SELECT "equipment".* FROM "equipment" INNER JOIN "exercise_equipment" ON "equipment"."id" = "exercise_equipment"."equipment_id" WHERE "exercise_equipment"."exercise_id" = $1  [["exercise_id", 2]]
  Rendered exercises/show.html.erb within layouts/application (122.7ms)
  User Load (60.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = 10  ORDER BY "users"."id" ASC LIMIT 1
  Rendered shared/_header.html.erb (61.9ms)
  Rendered shared/_alerts.html.erb (0.1ms)
Completed 200 OK in 264ms (Views: 21.3ms | ActiveRecord: 240.8ms)

【问题讨论】:

  • 这是您网络服务器的唯一输出吗?它应该告诉你它正在尝试做什么,即 SQL 查询、保存等。
  • 我在编辑中包含了来自我的服务器日志的完整 PATCH 跟踪,以及我的初始数据库状态。非常感谢您的观看!
  • 在您的嵌套表单中,它是 |e_form|,但您调用 ef.collection_select - 不应该是 e_form.collection_select 吗?
  • 嗯。我在维修票/零件使用场景中做了类似的事情。当我加载服务票并更改使用的部件时,它会执行一些 UPDATEDELETE 语句以在数据库中进行更改。我在这里看不到任何东西。我有点生疏,但我知道有多个复选框并更新您必须在表单中使用一些隐藏字段。
  • 你的 form_for 助手应该有@exercise。错字?

标签: javascript ruby-on-rails ruby forms ruby-on-rails-4


【解决方案1】:

首先,您需要确保正确定义关联。

任何has_many 关联都应使用复数名称定义 -

#app/models/exercise.rb
Class Exercise < ActiveRecord::Base
    has_many :exercise_equipments
    has_many :equipments, :through => :exercise_equipments

    accepts_nested_attributes_for :exercise_equipments
end

#app/models/exercise_equipment.rb
Class ExerciseEquipment < ActiveRecord::Base
   belongs_to :exercise
   belongs_to :equipment
end

#app/models/equipment.rb
Class Equipment < ActiveRecord::Base
   has_many :exercise_equipments
   has_many :exercises, through: :exercise_equipments
end

如果您已经开始使用它,并且对您所拥有的感到满意,那么我建议您保留当前的设置。不过,为了convention's缘故,您不妨采用上述方式

编辑我从删除的答案中看到 Beartech 对此进行了调查,结果发现 Rails 将 Equipment / Equipments 视为相同。值得忽略以上内容,但我将其留作将来参考


参数

我无法通过 我选择的集合元素。请参阅下面的模型,视图, 控制器和参数。

我想我明白你的意思 - 你正在寻找更新记录,但它不会将更新的参数发送到你的控制器,因此阻止它被更新。

虽然我看不到任何明显的问题,但我建议问题是您正在尝试填充 Exercise 对象的 exercise_id。您需要为exercise_equipment 对象定义它:

<%= f.fields_for :exercise_equipment do |eef| %>
   <%= eef.collection_select :equipment_id, Equipment.all, :id, :name %>
   <%= eef.check_box :is_optional %>
<% end %>

这将填充您的exercise_equipment 表,如下所述:

Time: 61.279 ms
postgres@=>db=# select id, exercise_id, equipment_id, optional from exercise_equipment;
 id | exercise_id | equipment_id | optional 
----+-------------+--------------+----------
  2 |           2 |            1 | t
(1 row)

目前,您正在使用 equipment_id 填充 Equipment 模型 - 这不起作用。以这种方式填充模型将服务器创建一条新记录,而不是更新已经创建的记录


额外字段

我想要一个链接来添加一个额外的设备字段,当它是 单击,类似于 Ryan Bates 在此 RailsCast 中的操作,但 他编写的辅助方法(如果您没有订阅,请参阅“显示注释”选项卡 查看源代码)似乎变得更加复杂 处理下面我的代码中显示的嵌套视图。任何帮助 处理这个?

这是一座难以克服的山

Ryan 在这个过程中使用了相当过时的方法(预先填充链接,然后让 JS 追加字段)。 “正确”的方法是构建一个新对象并从 ajax 附加 fields_for。听起来很难?那是因为它是:)

你是这样做的:

#config/routes.rb
resources :exercises do
   collection do
       get :ajax_update #-> domain.com/exercises/ajax_update
   end
end

#app/models/exercise.rb
Class Exercise < ActiveRecord::Base
   def self.build
       exercise = self.new
       exercise.exercise_equipment.build
   end
end

#app/controllers/exercises_controller.rb
Class ExercisesController < ApplicationController
   def new
       @exercise = Exercise.build
   end

   def ajax_update
      @exercise = Exercise.build
      render "add_exercise", layout: false #> renders form with fields_for
   end
end

#app/views/exercises/add_exercise.html.erb
<%= form_for @exercise do |f| %>
   <%= render partial: "fields_for", locals: { form: f } %>
<% end %>

#app/views/exercises/_fields_for.html.erb
<%= f.fields_for :exercise_equipment, child_index: Time.now.to_i do |eef| %>
    <%= eef.collection_select :equipment_id, Equipment.all, :id, :name  %>
    <%= eef.check_box :is_optional %>
<% end %>

#app/views/exercises/edit.html.erb
<%= form_for @exercise do |f| %>
    <%= render partial: "fields_for", locals: { form: f } %>
    <%= link_to "Add Field", "#", id: "add_field" %>
<% end %>

#app/assets/javascripts/application.js
$(document).on("click", "#add_field", function() {
    $.ajax({
       url: "exercises/ajax_update",
       success: function(data) {
          el_to_add = $(data).html()
          $('#your_id').append(el_to_add)
       }
    });
 });

【讨论】:

  • 您能否解释一下您编写的 javascript 中发生了什么?另外,有没有办法将它从 application.js 文件中移出到特定于练习的 coffescript 文件中?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-26
  • 2015-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多