Форма вложенных атрибутов 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