2009-04-22 5 views
1

Я пытаюсь понять, как ActiveRecord имеет дело с ассоциациями, которые сложнее, чем простые has_many, belongs_to и так далее.Сложные ассоциации в моделях ActiveRecord

В качестве примера рассмотрим приложение для записи музыкальных концертов. У каждого Gig есть группа, в которой есть жанр. У каждого концерта также есть место, в котором есть регион.

В грубом обозначениях MS Access (которые я вдруг начала чувствовать себя довольно ностальгическим для) эти отношения будут представлены, как это

 1 ∞  1 ∞  ∞ 1  ∞ 1 
Genre ---- Band ---- Gig ---- Venue ---- Region 

Я хотел бы иметь возможность узнать, например, , все группы, которые играли в регионе, или все места, где размещается определенный жанр.

В идеале, мои модели будут содержать этот код

class Genre 
    has_many :bands 
    has_many :gigs, :through => bands 
    has_many :venues, :through => :gigs, :uniq => true 
    has_many :regions, :through => :venues, :uniq => true 
end 

class Band 
    belongs_to :genre 
    has_many :gigs 
    has_many :venues, :through => :gigs, :uniq => true 
    has_many :regions, :through => :venues, :uniq => true 
end 

class Gig 
    belongs_to :genre, :through => :band 
    belongs_to :band 
    belongs_to :venue 
    belongs_to :region, :through => :venue 
end 

и так далее для Venue и Region.

Тем не менее, кажется, я должен производить что-то вроде этого вместо

class Genre 
    has_many :bands 
    has_many :gigs, :through => bands 
    has_many :venues, :finder_sql => "SELECT DISTINCT venues.* FROM venues " + 
    "INNER JOIN gigs ON venue.id = gig.venue_id " + 
    "INNER JOIN bands ON band.id = gig.band_id " + 
    "WHERE band.genre_id = #{id}" 
    # something even yuckier for regions 
end 

class Band 
    belongs_to :genre 
    has_many :gigs 
    has_many :venues, :through => :gigs, :uniq => true 
    # some more sql for regions 
end 

class Gig 
    delegate :genre, :to => :band 
    belongs_to :band 
    belongs_to :venue 
    delegate :region, :to => :venue 
end 

У меня есть два вопроса - один общий и один конкретный.

Общее:

я бы подумал, что я пытался сделать бы придумать довольно часто. Это то, что у меня есть на самом деле лучший способ сделать это, или есть что-то намного более простое, что я пропускаю?

Конкретная:

То, что я выше на самом деле не совсем работает! #{id} во второй жанровой модели действительно возвращает идентификатор класса. (Я думаю). Однако, похоже, это работает here и here

Я понимаю, что это довольно эпический вопрос, так что спасибо, если у вас это далеко. Любая помощь будет принята с благодарностью!

ответ

0

Для таких ассоциаций вы собираетесь написать собственный SQL-код - нет реального способа, которым вы можете справиться с цепочкой ассоциаций, как это, не делая каких-либо довольно массивных объединений, и на самом деле нет эффективный способ создания встроенных генераторов запросов с помощью однострочного интерфейса.

Вы также можете посмотреть: добавляет параметр ActiveRecord - это может делать то, что вы хотите.

3

Ассоциации предназначены для чтения и могут быть записаны в. Большая часть их стоимости является то, что вы можете сделать что-то вроде этого:

@band.gigs << Gig.new(:venue => @venue) 

Это звучит, хотя, как вы хотите что-то, что это только для чтения. Другими словами, вы хотите связать залы и жанры, но вы никогда не делать:

@venue.genres << Genre.new("post-punk") 

, потому что это не имеет смысла. У места есть только жанр, если у группы с этим конкретным жанром есть Gig.

Ассоциации не работают, потому что они должны быть доступны для записи. Вот как я бы сделал только для чтения ассоциации:

class Genre 
    has_many :bands 

    def gigs 
    Gig.find(:all, :include => 'bands', 
      :conditions => ["band.genre_id = ?", self.id]) 
    end 

    def venues 
    Venue.find(:all, :include => {:gigs => :band}, 
     :conditions => ["band.genre_id = ?", self.id]) 
    end 
end 
0

Похоже, работа для nested_has_many_through! Отличный плагин, который позволяет делать вложенные has_many :throughs

1

Вы можете добавить условия и параметры в свои ассоциации. Последние версии ActiveRecord дают мощность named_scopes, которая также будет работать с соответствующими записями.

С текущего проекта

Folder has_many Pages 
Page has_many Comments 

# In Page 
named_scope :commented, 
    :include => "comments", 
    :conditions => ["comments.id IS NULL OR comments.id IS NOT NULL"], 
    :order => "comments.created_at DESC, pages.created_at DESC" 

С помощью этого можно сказать:

folder.pages.commented 

Какой будет сфера на соответствующих записей, делая условным с прилагаемыми параметрами.

Плюс! named_scopes являются составными.

И еще прицелы:

named_scope :published, :conditions => ["forum_topics.status = ?", "published"] 

и цепь их вместе: folder.pages.published.commented