0

Я создал следующее, но без использования модели Rails. У меня есть следующие модели без каких-либо определенных отношений друг к другу. Tale Цель Культуры Мораль Значения Книги Ключевого символовActiveRecord -: принадлежит нескольким классам. Реконфигурирующее поведение

Я вручную хранить все данные отношения как массивы в таблице единых отношений со структурой следующего for is the table name/model name pluralized and for_id is the object id

Что я хотел бы сделать что-то вроде этого - Модель: Сказка -> has_many:: tale_relations Цель -> has_many:: tale_relations Культура -> h as_many:: tale_relations Мораль -> has_many:: tale_relations ценности -> has_many:: tale_relations Книги -> has_many:: tale_relations Ключевые слова -> has_many:: tale_relations Персонажи -> has_many:: tale_relations

Model: TaleRelation -> 
belongs_to: :tale 
belongs_to: :purpose 
belongs_to: :culture 
belongs_to: :morals 
belongs_to: :values 
belongs_to: :books 
belongs_to: :keywords 
belongs_to: :characters 

Но не хотелось бы, чтобы таблица tale_relations имела несколько столбцов для каждой из этих разных моделей. Я имею в виду, что я не хочу tale_id, purpose_id, moral_id и т. Д. (8 таких столбцов) Хотите иметь возможность идентифицировать записи через таблицу name (: для столбца) и id # (: for_id).

В настоящее время он работает вручную, но просто подумал, что код действительно становится чистым и простым и на самом деле расширяемым, если я могу получить эту работу. с двумя столбцами можно идентифицировать любые отношения.

ответ

0

Я подозреваю, что ваша проблема может стать намного проще, если вы наложите дополнительную структуру на свои модели. Тот факт, что вы называете свою модель TaleRelation, заставляет меня думать, что большинство из этих вещей должно быть напрямую связано с рассказом, которое можно было бы сделать с отношением has_and_belongs_to_many, тогда можно было бы связать другие вещи through: :tale. Но я думаю, что мы также можем решить вопрос, как было сказано.

Во-первых, вы хотите посмотреть на polymorphic associations. В коде, который вы указали выше, первым шагом будет использование belongs_to :relatable, polymorphic: true в TaleRelation и has_many :tale_relations, as: :relatable в других моделях. Это требует, чтобы таблица tale_relations имела два столбца relatable_id и relatable_type, для чего вы пытаетесь сделать это и for_id.

Однако, я думаю, вы действительно хотите сделать этот шаг дальше, а также избавиться от рассказов, символов, значений и т. Д. Столбцов. Мы можем сделать это, используя две полиморфные отношения, а не только одну.

Во-первых, структура таблицы (4 колонки):

create_table :tale_relations do |t| 
    t.integer :relater_id 
    t.string :relater_type 
    t.integer :relatee_id 
    t.string :relatee_type 
end 

И модель

class TaleRelation < ActiveRecord::Base 
    belongs_to :relater, polymorphic: true 
    belongs_to :relatee, polymorphic: true 

    after_create :create_inverse_relationship 
    after_destroy :destroy_inverse_relationship 

    def create_inverse_relationship 
    self.class.find_or_create_by(inverse_attributes) 
    end 

    def destroy_inverse_relationship 
    self.class.find_by(inverse_attributes).try(:destroy) 
    end 

    def inverse_attributes 
    { 
     relater_id: relatee_id, 
     relater_type: relatee_type, 
     relatee_id: relater_id, 
     relatee_type: relater_type 
    } 
    end 
end 

Что я делаю с обратными вызовами это каждый раз, когда вы создаете или уничтожить TaleRelation с микросхемой данный relater и relatee, он создает или уничтожает соответствующий обратный. Поэтому, если данная Сказка связана с Символом, из этого следует, что Символ также связан с Сказкой. Если вам не нужна эта симметрия, просто выньте эти обратные вызовы.

Сейчас, к примеру, в Сказке:

class Tale < ActiveRecord::Base 
    has_many :tale_relations, as: :relater, dependent: :destroy 

    has_many :characters, through: :tale_relations, source: :relatee, source_type: "Character" 
    has_many :values, through: :tale_relations, source: :relatee, source_type: "Value" 
    # and so on 
end 

И подобные вещи в ценности и характера и так далее. Теперь Tale has_many: символы, не требующие сохранения своих идентификаторов в виде разделенной запятой строки или массива, поскольку похоже, что вы делали это выше в своей таблице. Обязательно не включайте какие-либо самореферентные инструкции has_many (т. Е. Не помещайте has_many :characters в символ)

Таким образом, мы определяем связь с TaleRelation через полиморфные relater_id и relater_type, поэтому мы можем иметь все эти отношения без всех столбцов , И через эти отношения мы можем собрать (полиморфно!) Все отношения правильного класса. Это дает мне:

2.2.2 > t = Tale.create(name: "T") 
=> #<Tale id: 1, name: "T"> 
2.2.2 > c = Character.create(name: "C") 
=> #<Character id: 1, name: "C"> 
2.2.2 > d = Character.create(name: "D") 
=> #<Character id: 2, name: "D"> 
2.2.2 > v = Value.create(name: "V") 
=> #<Value id: 1, name: "V"> 
2.2.2 > t.characters << c 
=> #<ActiveRecord::Associations::CollectionProxy [#<Character id: 1, name: "C">]> 
2.2.2 > v.characters << c 
=> #<ActiveRecord::Associations::CollectionProxy [#<Character id: 1, name: "C">]> 
2.2.2 > v.characters << d 
=> #<ActiveRecord::Associations::CollectionProxy [#<Character id: 1, name: "C">, #<Character id: 2, name: "D">]> 
2.2.2 > d.values 
=> #<ActiveRecord::Associations::CollectionProxy [#<Value id: 1, name: "V">]> 
2.2.2 > t.characters.first.values 
=> #<ActiveRecord::Associations::CollectionProxy [#<Value id: 1, name: "V">]> 

Обратите внимание, что это НЕ распространяет отношения второго порядка. I.e.,

2.2.2 > t.values 
=> #<ActiveRecord::Associations::CollectionProxy []> 

, хотя t имеет символ со значением. Как только вы начнете это делать, довольно скоро у вас будет все, что связано со всем остальным.