Форма вложенных атрибутов Rails для полиморфных / одиночных таблиц наследования

Я работаю над формой (используя SimpleForm), которая позволяет редактировать встроенные ассоциации. Проблема, с которой я сталкиваюсь, заключается в том, что вложенные модели являются подклассами, поэтому они представляют собой разные типы с потенциально разными полями. Я создаю скрытые формы для каждого типа модели и использую JavaScript для отображения формы для выбранного типа.

К вашему сведению, я использую следующие драгоценные камни:

  • Рельсы 3.2
  • Mongoid
  • Простая форма

Вот упрощенный пример того, что я имею до сих пор:

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, используя существующие помощники форм?

2 ответа

Я думаю, что у меня была похожая проблема, однако вместо has_one отношения, у меня есть has_many,

В основном я использовал cocoon гем для динамического добавления полей для каждого декорированного класса (например, Car или же Truck) и затем я 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_fields частично проверяешь что это за объект (Car или же Truck) и вы делаете правильные поля.

Я думаю, что это работает довольно хорошо, и было именно то, что мне было нужно. Надеюсь, поможет.

Я написал более подробное объяснение по адресу: http://www.powpark.com/blog/programming/2014/05/07/rails_nested_forms_for_single_table_inheritance_associations

Это одна из тех ситуаций, когда прямое сопоставление формы с моделью не является идеальным. Я думаю, что заполненная пользователем карта формы и экземпляр модели постоянства - это две совершенно разные концепции.

Вы можете попытаться создать подкласс 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
Другие вопросы по тегам