如果对象在 Rails 6 中以嵌套形式不存在,如何创建对象?

Posted

技术标签:

【中文标题】如果对象在 Rails 6 中以嵌套形式不存在,如何创建对象?【英文标题】:How to create an object if it does not exist in a nested form in Rails 6? 【发布时间】:2020-12-30 07:04:02 【问题描述】:

我需要创建一个Training,然后在 Rails 6 应用程序中添加带有嵌套表单字段(茧)的Attendances。情况如下:

型号如下:

训练模型

class Training < ApplicationRecord  
  has_many :attendances, dependent: :destroy
  has_many :people, through: :attendances
  accepts_nested_attributes_for :attendances, reject_if: :all_blank, allow_destroy: true
end

考勤模式

class Attendance < ApplicationRecord
  belongs_to :training
  belongs_to :person
end

人物模型

class Person < ApplicationRecord
  has_many :attendances, dependent: :destroy
  has_many :trainings, through: :attendances
end

如果数据库中存在Person,则用户从下拉选择字段中选择此人的姓名。

如果Person 不存在,则用户必须通过输入此人的姓名和年龄来手动输入此人的信息。

如何设置控制器和表单以允许用户手动输入人员的姓名和年龄,然后在保存培训时在嵌套表单字段中自动创建该人员和出勤?强>

【问题讨论】:

这是我在这里描述的示例之一:github.com/nathanvda/cocoon/wiki/…,并且也在示例项目github.com/nathanvda/cocoon_simple_form_demo中进行了演示 这些链接显示了如何做到这一点。对于这种情况,它是 wiki 末尾的 has_many 到示例的最后一个示例。 【参考方案1】:

关键在于嵌套属性有一个id 字段。 Rails 的行为是在 id 存在时更新现有记录,或者在 id 为空时创建新记录。

本质上,这就是您需要做的所有事情。输入此人的姓名和年龄,但将 id 留空,系统将创建一条新记录。

我强烈推荐cocoon gem https://github.com/nathanvda/cocoon,它将为您的培训表单提供link_to_add_association 助手,自动让您打开一个新的空人表单。实施后,您将使用类似这样的方式调用帮助程序

<%= link_to_add_association 'add attendee', f, :people %>

您可以忽略此人通过联接表与培训相关联...当您保存新人员时,rails 将自动创建联接记录。

【讨论】:

是的,但这里的情况是我的茧链接是这样的:link_to_add_association '添加出勤率', f, :attendances。因此,我正在创建一个出勤(在本例中为连接表)并同时在培训表单中创建一个人。 我认为这需要使用first_or_create方法 不要做f.attendancef.people你可以忽略出勤,只加人。【参考方案2】:

我使用了上面评论中的链接:https://github.com/nathanvda/cocoon/wiki/A-guide-to-doing-nested-model-forms#the-look-up-or-create-belongs_to

我使用了 wiki 中的最后一个示例。

以下是我的 MVC 最终版本针对此功能的结果:

模型

class Person < ApplicationRecord
  has_many :attendances, dependent: :destroy
  has_many :trainings, through: :attendances
end

class Attendance < ApplicationRecord
  belongs_to :training
  belongs_to :person
  accepts_nested_attributes_for :person, :reject_if => :all_blank
end

class Training < ApplicationRecord  
  has_many :attendances
  has_many :people, through: :attendances
  accepts_nested_attributes_for :attendances, reject_if: :all_blank, allow_destroy: true
  accepts_nested_attributes_for :people
end

训练控制器

class TrainingsController < ApplicationController
    
    put your normal controller actions here and then in 
    training_params put this:

    def training_params
      params.require(:training).permit(
        attendances_attributes: [:id, :_destroy, :person_id, person_attributes: [:id, :_destroy, :name, :age, :program_id]]
      )
    end
end

将此添加到培训/_form

  .row
    .col.s12
      .pt-5
        %h6
          Attendance
      #people
        = f.fields_for :attendances do |attendance|
          = render 'attendance_fields', :f => attendance
        .people_links
          = link_to_add_association 'Add', f, :attendances

将此添加到培训/_attendance_fields

.nested-fields.attendance-person-fields
  .field
     .person_from_list
       = f.select(:person_id, Person.all.map  |p| [p.name, p.id] ,  include_blank: true ,  :class => 'chosen-select' )
     = link_to_add_association 'Or create', f, :person, data: disable_with: "creating person"

终于在培训/_person_fields

.nested-fields
  .input-field
    = f.text_field :name
    = f.label :name
  .input-field
    = f.number_field :age
    = f.label :age

我添加了咖啡脚本:(在:person.coffee

  $('#people').on('cocoon:before-insert', (e, attendance_to_be_added) ->
    attendance_to_be_added.fadeIn 'slow'
    return
  )

  $('#people a.add_fields').data('association-insertion-position', 'before').data 'association-insertion-node', 'this'
  
  $('#people').on 'cocoon:after-insert', ->
    
    $('.attendance-person-fields a.add_fields').data('association-insertion-position', 'before').data 'association-insertion-node', 'this'
    
    $('.attendance-person-fields').on 'cocoon:after-insert', ->
      
      $(this).children('.person_from_list').remove()
      $(this).children('a.add_fields').hide()

      return
    return

【讨论】:

以上是关于如果对象在 Rails 6 中以嵌套形式不存在,如何创建对象?的主要内容,如果未能解决你的问题,请参考以下文章

如果 Rails 表单中的另一个字段为空,则验证嵌套字段

如何处理对尚不存在的对象的 ID 引用的 RestKit 嵌套数组?

通过复选框Rails 4添加多个嵌套属性(可能有多种形式)

如何在 Spring Boot Rest API 中以 XML 形式返回对象列表

Rails 4 - 根据嵌套形式的第一个选择菜单中的选择动态填充第二个选择菜单

Rails has_many :通过简单形式的嵌套形式