Взаимоотношение моделей в Rails, часть вторая
Февраль 22, 2010

Первая часть тут, а мы продолжаем, мвахахаха! Нам еще бы разобраться где ставить belongs_to, что выбрать has_many :through или has_and_belongs_to_many, как реализуются полиморфные ассоциации, ну и наконец, не забыть про вложенные (nested) ассоциативные связи. Поехали!
2.7 Где ставить belongs_to, а где has_one
Если ты, дорогой читатель, создаешь связь один-к-одному, то тебе понадобится добавить belongs_to в одну модель, а has_one в другую. Но как узнать в какую модель что добавлять?
Различие заключается лишь в том, где находится внешний индексный ключик (он находится в таблице класса, в котором объявляется belongs_to связь). Связь has_one говорит о том что ты имеешь нечто из множества, а не наоборот. К примеру, куда больший смысл в фразе: «у заказчика есть счет», чем «счет обладает заказчиком (как владелецем)». Посему и связь будет выглядеть вот так:
class Supplier < ActiveRecord::Base has_one :account end class Account < ActiveRecord::Base belongs_to :supplier end
А соответсвующая миграция будет выглядеть следующим образом:
class CreateSuppliers < ActiveRecord::Migration def self.up create_table :suppliers do |t| t.string :name t.timestamps end create_table :accounts do |t| t.integer :supplier_id t.string :account_number t.timestamps end end def self.down drop_table :accounts drop_table :suppliers end end
шспользуя t.integer :supplier_id, мы задаем внешний индексный ключик очевидным и конкретным способом. В нынешней версии рельсов, можно абстрагироваться от реализации деталей и использовать вместо id ссылку на модель через t.references :supplier.
2.8 Выбирая между has_many :through и has_and_belongs_to_many
Рельсы предоставляют два разных способа реализации связи много-к-многому. Простейший способ – использовать has_and_belongs_to_many (кстати, для сокращения часто используют аббревиатуру habtm), который позволяет делать ассоциацию напрямую:
class Assembly < ActiveRecord::Base has_and_belongs_to_many :parts end class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end
Второй способ реализации связи много-к-многому – это использовать has_many :through. В данном случае связь получается не на прямую, а через дополнительную соединяющую модель:
class Assembly < ActiveRecord::Base has_many :manifests has_many :parts, :through => :manifests end class Manifest < ActiveRecord::Base belongs_to :assembly belongs_to :part end class Part < ActiveRecord::Base has_many :manifests has_many :assemblies, :through => :manifests end
Выбрать способ реализации просто: используй связь через has_many :through, если нужно работать со связующей моделью отдельно. Если этого не требуется, то проще создать связь через has_and_belongs_to_many (только не забудь создать соединяющую таблицу в базе данных).
А так же используй has_many :through, если хочешь пользоваться валидаторами, обратными вызовами (колбэками), или же требуются дополнительные аттрибуты в соединяющую модель.
2.9 Полиморфные ассоциации
Чуть более продвинутый вариант ассоциаций – это полиморфные ассоциации. В данном типе связи модель может принадлежать более чем одной другой модели, как это при обычных связях. К примеру, есть модель картинки, которая может принадлежать как работнику (аватарка, вестимо), а может принадлежать продукту, что продается. Реализовать можно так:
class Picture < ActiveRecord::Base belongs_to :imageable, :polymorphic => true end class Employee < ActiveRecord::Base has_many :pictures, :as => :imageable end class Product < ActiveRecord::Base has_many :pictures, :as => :imageable end
Объявление belongs_to в полиморфных ассоциациях сродни созданию интерфейса, который могут использовать другие модели. Через экземпляр модели работника можно получать коллекцию картинок: @employee.pictures. Таким же способом можно получить картинки продуктов: @product.pictures.
Если у вас есть экземплер класса Picture, можно добраться до родителей по связи через @picture.imageable. Чтобы это работало, необходимо объявить две колонки индексных ключей: одна содержит идентификатор в таблице модели родителя, другая содержит тип модели родителя:
class CreatePictures < ActiveRecord::Migration def self.up create_table :pictures do |t| t.string :name t.integer :imageable_id t.string :imageable_type t.timestamps end end def self.down drop_table :pictures end end
Но о таких сложностях можно забыть, если использовать форму t.references. Тогда всегда, как обычно в таблице у модели которой есть belongs_to пишем t.references, и проблем как небывало:
class CreatePictures < ActiveRecord::Migration def self.up create_table :pictures do |t| t.string :name t.references :imageable, :polymorphic => true t.timestamps end end def self.down drop_table :pictures end end

2.10 Вложенные ассоциации
В процессе проектирования модели данных, приходится сталкиваться с моделями, которые имеют связи с самими собой. К примеру, логично держать всех сотрудников в одной модели, связанной с одной таблицей данных, но так же необходимо отслеживать связи между управляющими и подчиненными. Давай рассмотрим реализацию связи для вложенной ассоциации:
class Employee < ActiveRecord::Base has_many :subordinates, :class_name => "Employee", :foreign_key => "manager_id" belongs_to :manager, :class_name => "Employee" end
При такой реализации, вызов подчиненных и руководителей будет выглядеть так: @employee.subordinates и @employee.manager.

Февраль 24th, 2010 at 2:18 дп
бедная-бедная буковка Ш
Февраль 26th, 2010 at 2:10 дп
[...] связи есть и как они записываются, даль�?е – легче! Продолжение Автор willson Категория Теория программирования 2 [...]