Rails STI с использованием ОДНОЙ формы
-
24-09-2019 - |
Вопрос
У меня есть форма, которая позволяет мне добавлять в поток файлы разных форматов.Итак, поток состоит из множества файлов. Эти файлы являются XML-файлами, но в основном имеют разные схемы.У меня есть одна форма, которая позволяет пользователю добавлять любой файл, который он хочет. Я использую STI (который отлично работает, когда данные уже есть в таблице), моя проблема заключается в добавлении данных в таблицу.
Форма имеет одно поле ввода, просто file_field, которое позволяет пользователю выбрать файл, который он хочет загрузить.Поскольку у меня есть только одна форма, я не могу создать экземпляр правильного объекта, мне приходится делать это программно.и я не уверен, как это сделать.
Должен ли я просто (или могу ли я) добавить раскрывающийся список с возможными типами и назвать это поле «типом», чтобы при отправке формы рельсы создавали экземпляр типа записи объекта, поскольку предоставлен атрибут типа?
Какова наилучшая практика для этого..Я использую рельсы 2.3.4.
Решение
Я нашел решение на http://coderrr.wordpress.com/2008/04/22/building-the-right-class-with-sti-in-rails/#comment-1826
class GenericClass < ActiveRecord::Base
class << self
def new_with_cast(*a, &b)
if (h = a.first).is_a? Hash and (type = h[:type] || h['type']) and (klass = type.constantize) != self
raise "wtF hax!!" unless klass < self # klass should be a descendant of us
return klass.new(*a, &b)
end
new_without_cast(*a, &b)
end
alias_method_chain :new, :cast
end
end
Это отлично сработало для меня с минимальным количеством кода - я не знаю, хакерское оно или нет, но оно работает и довольно чистое.Мне понравилось то, что это всего лишь 10 строк кода.
Другие советы
Я не знаю, сколько у вас типов, но в прошлом я просто использовал отдельные контроллеры и представления для разных типов.Таким образом, вы не создаете новый объект базового класса и не пытаетесь установить тип, а просто используете модель, которая наследуется от базового класса.Каждая новая/редактируемая страница ваших ресурсов может отображать общий фрагмент в блоке form_for.Частичный файл будет содержать ваше file_field.
Таким образом, когда форма будет отправлена, она перейдет к правильному контроллеру, вызовет правильный ресурс.new, и все будет в порядке.
Недостатком, конечно, является большее количество файлов, и на какую бы страницу вы ни ссылались на «добавить новый файл», вам нужно добавить несколько ссылок, таких как «добавить новый файл этого типа», «добавить новый файл этого типа» и т. д.
Что касается установки типа в форме, я не уверен, работает ли это, сомневаюсь, но просто попробуйте (дайте нам знать).Вы можете сделать этот тип выпадающим из select_tag и при его изменении использовать Javascript для изменения местоположения действия в форме.
Отредактировано и добавлено базовое решение
Не то чтобы мне нравилось это решение, и я сомневаюсь, что оно ни в коем случае не лучшее, но если вам действительно не нужны отдельные контроллеры и вам нужно, чтобы оно работало, вы можете сделать что-то вроде этого:
class XmlFile < ActiveRecord::Base
end
class XmlFileTypeA < XmlFile
end
class XmlFileTypeB < XmlFile
end
def create
# Leaving this case statement in the controller for simplicity but you probably want to move this to the model
case params[:chosen_xml_type]
when "file_type_a"
@item = XmlFileTypeA.new(params)
when "file_type_b"
@item = XmlFileTypeB.new(params)
else
raise "Unknown file type!"
etc
end
end