associação STI e has_many com a coluna “tipo” como Key
-
05-07-2019 - |
Pergunta
Eu estou usando Herança de Tabela Única para o gerenciamento de diferentes tipos de projetos. Eu decidi armazenar algumas informações associadas a cada tipo de projeto. Então, eu criei novos tabela "project_types" com o campo "model_type" como chave primária. valores de chave primária são valores de campo "tipo" de mesa "projectos". Problema : Quando eu tentar obter associado com ProjectTypes objeto de projeto objeto que ele sempre retorna null.
>> p = Project.find(:first)
=> #<SiteDesign id: 1, type: "SiteDesign", name: "1", description: "dddd", concept: "d", client_id: 40, created_at: "2009-10-15 08:17:45", updated_at: "2009-10-15 08:17:45">
>> p.project_type
=> nil
Começar projetos associados com projecto ProjectTypes é OK. Existe maneira de fazer ele funciona corretamente?
Models:
class Project < ActiveRecord::Base
belongs_to :project_type, :class_name => "ProjectTypes", :foreign_key => "model_name"
end
class SiteDesign < Project
end
class TechDesign < Project
end
class ProjectTypes < ActiveRecord::Base
self.primary_key = "model_name"
has_many :projects, :class_name => "Project", :foreign_key => "type"
end
Migrações:
class CreateProjectTypes < ActiveRecord::Migration
def self.up
create_table :project_types, :id => false do |t|
t.string :model_name , :null => false
t.string :name, :null => false
t.text :description
t.timestamps
end
add_index :project_types, :model_name, :unique => true
#all project types that are used.
models_names = {"SiteDesign" => "Site design",
"TechDesign" => "Tech design"}
#key for model_name and value for name
models_names.each do |key,value|
p = ProjectTypes.new();
p.model_name = key
p.name = value
p.save
end
end
def self.down
drop_table :project_types
end
end
class CreateProjects < ActiveRecord::Migration
def self.up
create_table :projects do |t|
t.string :type
t.string :name
t.text :description
t.text :concept
t.integer :client_id
t.timestamps
end
end
def self.down
drop_table :projects
end
end
Solução
Não é de estranhar que você está recebendo problemas. Movendo-se de um sistema STI pura para o seu sistema atual que você está terrivelmente quebrar os padrões que você está usando, misturando partes de um com peças de outro.
Eu pessoalmente ir para algo como:
class Project < ActiveRecord::Base
attr_readonly(:project_type)
belongs_to :project_type
before_create :set_project_type
def set_project_type()
project_type = ProjectType.find_by_model_name(this.class)
end
end
class SiteProject < Project
end
class TechProject < Project
end
class ProjectType < ActiveRecord::Base
has_many :projects
end
com as migrações:
class CreateProjectTypes < ActiveRecord::Migration
def self.up
create_table :project_types do |t|
t.string :model_name , :null => false
t.string :name, :null => false
t.text :description
t.timestamps
end
add_index :project_types, :model_name, :unique => true
#all project types that are used.
models_names = {"SiteDesign" => "Site design",
"TechDesign" => "Tech design"}
#key for model_name and value for name
models_names.each do |key,value|
p = ProjectTypes.new();
p.model_name = key
p.name = value
p.save
end
end
def self.down
drop_table :project_types
end
end
class CreateProjects < ActiveRecord::Migration
def self.up
create_table :projects do |t|
t.string :type
t.references :project_type, :null => false
t.text :description
t.text :concept
t.integer :client_id
t.timestamps
end
end
def self.down
drop_table :projects
end
end
Ele só limpa as coisas e ele também ajuda a esclarecer o que você está fazendo. Sua mesa 'ProjectType' é puramente para dados extra, a sua árvore de herança ainda existe. Eu também jogado em algumas verificações para garantir que o tipo de projeto é sempre definido (e corretamente, com base no nome do modelo) e você pára de mudar tipo de projeto uma vez que foi salvo por fazer o atributo somente leitura.