Pergunta

Eu estou usando herança única tabela para o meu pedido. Meu tipo polimórfico é de manutenção com apenas um subtipo, agora, chamado OilChange. Eu estou correndo em problemas criando meus registros no meu método criar no controlador. Aqui está o código.

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

Os params [: manutenção] de hash tem teclas {: nome, tipo:}. Posso verificar a sua existência e os valores, imprimindo-as da seguinte maneira

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

Se eu passar em "OilChange" para o valor do: tipo de chave, o registro de manutenção é do tipo de manutenção e não da OilChange. Posso verificar se por encontrar o registro no console REPL. O campo de tipo é nulo. Eu posso fazê-lo funcionar como eu quero, adicionando a seguinte linha.

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

Mas isso é feio. O que eu estou querendo saber é por isso que não o método de criação defina o campo Tipo como faz o campo de nome apenas encontrar?

Os dois tipos que você vê olhar como este em minha 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"

As minhas modelos parecido com este.

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!

Foi útil?

Solução

A resposta específica é que o atributo type, como muitos dos atributos especiais do Rails, é protegido de atribuição em massa. (Olhe para cima :attr_protected na documentação.)

A resposta para o problema mais geral é que você não está confiando seus modelos suficiente. Se você deseja criar um registro do tipo OilChange, você não deve chamar Maintenance.new ou Maintenance.create. OilChange.new chamada ou OilChange.create vez, e Rails irá automaticamente cuidar de definir o tipo e fazer todo o trabalho de fundo para você.

Outras dicas

Tente o seguinte código:

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

Se você quer ter apenas subclasses de Manutenção em seu banco de dados, faça o seguinte na sua classe Manutenção:

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

Este código irá criar uma classe de manutenção inválida quando os parâmetros contêm o tipo errado. Se eles contêm o tipo certo, uma instância da classe especificada será criada.

Se os seus modelos usam namespaces que você pode olhar para este artigo em vez de usar Module.const_get:

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top