【问题标题】:Rails nested attributes form for polymorphic/single table inheritance associations用于多态/单表继承关联的 Rails 嵌套属性形式
【发布时间】:2013-07-01 19:43:07
【问题描述】:

我正在开发一个允许您编辑嵌入式关联的表单(使用 SimpleForm)。我遇到的问题是嵌套模型是子类,因此它们是具有潜在不同字段的不同类型。我正在为每种类型的模型创建隐藏表单,并使用 JavaScript 来显示所选类型的表单。

仅供参考,我正在使用以下宝石:

  • Rails 3.2
  • 蒙古人
  • 简单表单

这是我目前所拥有的一个简化示例:

class Garage
  include Mongoid::Document
  embeds_one :vehicle
  accepts_nested_attributes_for :vehicle
end

class Vehicle
  include Mongoid::Document
  embedded_in :garage
  attr_accessible :_type
end

class Car < Vehicle
  field :car_field
  attr_accessible :car_field
end

class Truck < Vehicle
  field :truck_field
  attr_accessible :truck_field
end

在控制台中:

> garage = Garage.new
> garage.vehicle = Car.new(car_field: 'something')
> garage.save!

形式:

= simple_form_for @garage do |f|
  = f.input :vehicle do |vehicle_form|
     = vehicle_form.input :_type, collection: ['Car', 'Truck']

  %span.hide{data:{fields-for:'Car'}}
    = vehicle_form.input :car_field

  %span.hide{data:{fields-for:'Truck'}}
    = vehicle_form.input :truck_field

:coffeescript
  $('#garage_vehicle_attributes__type').change ->
    type = $(@).find('option:selected').val()
    $('[data-fields-for="' + type + '"]').show()

这个例子中会出现的问题是它不能渲染truck_field,因为Car没有truck_field方法。除了抛出任何表单助手并手动管理 html 和字段值之外,我不确定如何解决这个问题。即使在谷歌上搜索了很多,我也找不到这种类型的任何例子。

如何使用现有的表单助手以标准的“Rails 方式”解决这个问题?

【问题讨论】:

  • 也许这会有所帮助:stackoverflow.com/questions/3327236/…(但不确定)
  • @jethroo 谢谢,但不幸的是,答案涵盖了我的代码示例中已有的所有内容。他们省略了为不同模型处理不同字段的最后一步。
  • 如果基础文档没有任何公共字段,我会考虑创建两组字段,一组用于卡车(带有新的空白实例),一组用于汽车,设置@ 987654328@ 在车辆类型切换和创建新的卡车文件。如果这对您不起作用或看起来太不优雅,其他选择是将所有字段移动到基本文档,因为如果您将汽车转换为卡车,我相信 mongoid 不会取消设置 car_field 并且您在 db 中的文档将具有比它需要。切换文档类型的正确方法是分配新文档而不是编辑现有的恕我直言。
  • @rubish 很好的提示,我也发现了。我也有一个embeds_many 关联,所以解决方案有点不同。
  • 我认为 embeds_many 比嵌入一个相对容易处理,因为您可以使用现有的嵌套属性机制在一次调用中销毁旧文档并创建新文档。但是,使用 embeds_one,您将需要创建新文档并覆盖现有文档,并确保在销毁文档时需要进行任何清理(例如删除 s3 文件),它会得到妥善处理。

标签: ruby-on-rails ruby-on-rails-3 mongoid nested-attributes


【解决方案1】:

我想我遇到了类似的问题,但是我没有has_one 关系,而是has_many

基本上我使用cocoon gem 为每个装饰类动态添加字段(例如CarTruck),然后我accept_nested_attributes_for :vehicle。表单是动态构建的,提交时,参数进入vehicle_attributes

代码看起来像(为has_one 关联更新):

# _form.html.haml

= simple_form_for @garage, :html => { :multipart => true } do |f|

  = f.simple_fields_for :vehicles do |vehicle|
    = render 'vehicle_fields', :f => item
    = link_to_add_association 'Add a Car', f, :vehicles, :wrap_object => Proc.new { |vehicle| vehicle = Car.new }
    = link_to_add_association 'Add a Truck', f, :vehicles, :wrap_object => Proc.new { |vehicle| vehicle = Truck.new }

= f.button :submit, :disable_with => 'Please wait ...', :class => "btn btn-primary", :value => 'Save' 

然后在_vehicle_fieldspartial 中检查它是什么对象(CarTruck)并渲染正确的字段。

我认为这很有效,而且正是我所需要的。希望对您有所帮助。

我在http://www.powpark.com/blog/programming/2014/05/07/rails_nested_forms_for_single_table_inheritance_associations写了一个更长的解释

【讨论】:

    【解决方案2】:

    这是直接将表单映射到模型并不理想的情况之一。我认为用户填充的表单映射和持久性模型实例是两个截然不同的概念。

    您可以尝试将 Vehicle 子类化为用于接受表单数据的类。然后混合处理表单特定内容所需的所有额外代码。这样,您就可以保持 Vehicle 模型干净。您还可以覆盖VehicleFormModel 中的方法以像工厂一样工作,以便在创建对象时构建正确的实例。在您的控制器中,实例化 VehicleFormModel 而不是 Vehicle。

    class VehicleFormModel < Vehicle
      include Car::FormModel
      include Truck::FormModel
    
      def self.build
        # Use a form field to handle specifics for each type,
        # delegating to the mixed in FormModel as needed
      end
    
    end
    
    class Car < Vehicle
      module FormModel
        def self.included(base)
          base.class_eval do
            field :car_field
            attr_accessible :car_field
          end
        end
      end
    end
    

    【讨论】:

    • 我看到有一个 Railscast 可能涵盖这个:railscasts.com/episodes/416-form-objects
    • 有趣的想法。不过,我希望看到更多的 HTML/JavaScript 实现。
    • 又看了一遍RailsCast,很全面。
    • 我已经看过截屏视频了。我指的是这种类型的表单的细节,它会根据您选择的前一个字段创建不同的字段。
    猜你喜欢
    • 2015-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多