STI и ассоциация has_many со столбцом “type” в качестве ключа
-
05-07-2019 - |
Вопрос
Я использую наследование одной таблицы для управления различными типами проектов.Я решил сохранить некоторую информацию, связанную с каждым типом проекта.Итак, я создал новую таблицу "project_types" с полем "model_type" в качестве первичного ключа.Значения первичного ключа - это значения поля "тип" таблицы "проекты". Проблема:Когда я пытаюсь связать объект Project с объектом ProjectTypes, он всегда возвращает 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
Получение проектов, связанных с проектом ProjectTypes, в порядке.Есть ли способ заставить его работать должным образом?
Модели:
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
Миграции:
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
Решение
Неудивительно, что у вас возникают проблемы.Переходя от чистой системы STI к вашей текущей системе, вы ужасно нарушаете используемые вами шаблоны, смешивая части одного с частями другого.
Лично я бы предпочел что-то вроде:
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
с миграциями:
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
Это просто проясняет ситуацию, а также помогает прояснить, что вы делаете.Ваша таблица 'ProjectType' предназначена исключительно для дополнительных данных, ваше дерево наследования все еще существует.Я также ввел некоторые проверки, чтобы убедиться, что тип вашего проекта всегда задан (и правильно, на основе названия модели) и не позволяет вам изменять тип проекта после его сохранения, сделав атрибут доступным только для чтения.