Первая часть тут, а мы продолжаем, мвахахаха! Нам еще бы разобраться где ставить 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.

2 Комментари(я/ев) to “Взаимоотношение моделей в Rails, часть вторая”

  1. анонимус Says:

    бедная-бедная буковка Ш

  2. /etc/reflection – размышления на тему… » Blog Archive » Взаимоотношения моделей в Rails Says:

    [...] связи есть и как они записываются, даль�?е – легче! Продолжение Автор willson Категория Теория программирования 2 [...]

Leave a Reply

Нам помогают
Дружественные сайты: