Создание записей наследования одной таблицы в Rails

Я использую наследование одной таблицы для своего приложения. Мой полиморфный тип - Maintenance с одним подтипом, который сейчас называется OilChange. У меня проблемы с созданием моих записей в моем методе создания в контроллере. Вот код.

@log = Log.new(params[:log])
@log.maintenance = Maintenance.new(params[:maintenance])

Хэш params [: maintenance] имеет ключи {: name,: type}. Я могу проверить их существование и ценности, распечатав их следующим образом

print params[:maintenance][:name]
print params[:maintenance][:type]

Если я передаю "OilChange" в качестве значения ключа: type, запись обслуживания будет иметь тип обслуживания, а не OilChange. Я могу убедиться в этом, найдя запись в консоли REPL. Поле типа равно нулю. Я могу заставить его работать так, как хочу, добавив следующую строку.

@log.maintenance.type = params[:maintenance][:type]

Но это некрасиво. Что мне интересно, так это почему метод create не устанавливает поле типа, как только находит поле имени?

Два типа, которые вы видите, выглядят так в моем schema.rb

create_table "logs", :force => true do |t|
  t.date     "date"
  t.text     "description"
  t.string   "title"
  t.string   "summary"
  t.integer  "car_id"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.integer  "maintenance_id"
  t.integer  "mileage"
end

create_table "maintenances", :force => true do |t|
  t.string   "name"
  t.string   "type"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.string   "oil_brand"
  t.string   "oil_type"
  t.string   "oil_filter_type"

Мои модели выглядят так.

class Log < ActiveRecord::Base
belongs_to :car
has_and_belongs_to_many :tags
  belongs_to :maintenance
end

class Maintenance < ActiveRecord::Base
  has_one :log
end

class OilChange < Maintenance
end

TIA!


person dharga    schedule 21.10.2009    source источник


Ответы (2)


Конкретный ответ заключается в том, что атрибут type, как и многие специальные атрибуты Rails, защищен от массового назначения. (Посмотрите :attr_protected в документации.)

Ответ на более общую проблему заключается в том, что вы недостаточно доверяете своим моделям. Если вы хотите создать запись типа OilChange, вы не должны вызывать Maintenance.new или Maintenance.create. Вместо этого вызовите OilChange.new или OilChange.create, и Rails автоматически позаботится об установке типа и выполнит всю фоновую работу за вас.

person SFEley    schedule 21.10.2009
comment
Я бы с удовольствием это сделал, но тип обслуживания зависит от ввода пользователя. я просто не знаю, что это будет во время компиляции - person dharga; 21.10.2009
comment
Неважно. Руби динамична. ›8-› Допустим, у вас есть раскрывающийся список «Тип обслуживания». Просто верните значения из этого раскрывающегося списка, например OilChange, и вы можете вызвать params[:maintenance_type].constantize.new, чтобы получить свой объект. (constantize - это метод, предоставляемый ActiveSupport.) - person SFEley; 21.10.2009
comment
Или вы можете явно указать тип: maintenance.type = OilChange - person Nikos D; 17.02.2011

Попробуйте следующий код:

begin
  klass = Module.const_get(params[:maintenance][:type])
  @log.maintenance = klass.new(params[:maintenance])
  if ! @log.maintenance.is_a?(Maintenance)
    @log.maintenance = Maintenance.new(params[:maintenance])
  end
rescue NameError
  @log.maintenance = Maintenance.new(params[:maintenance])
end

Если вы хотите, чтобы в вашей базе данных были только подклассы обслуживания, сделайте следующее в своем классе обслуживания:

class Maintenance < ActiveRecord::Base
  validates_presence_of :type
  # other stuff goes here
end

Этот код создаст недопустимый класс обслуживания, если параметры содержат неправильный тип. Если они содержат правильный тип, будет создан экземпляр указанного класса.

Если в ваших моделях используются пространства имен, вы можете изучить эту статью вместо использования Module.const_get:

person chiborg    schedule 10.04.2011