Search code examples
ruby-on-railsdatabase-designhas-manybelongs-to

How to create "two-side" many-to-many relationships in Rails?


Suppose we have a photography site. Any author can subscribe to receive updates from any other author. Obviously if author A is subscribed to author B that doesn't mean that B is subscribed to A. So we build models

class Author < ActiveRecord::Base
  has_many :subscriptions
  has_many :subscribed_by_author, :through => :subscriptions, :source => :subscribed_to
end

class Subscription < ActiveRecord::Base
  belongs_to :author
  belongs_to :subscribed_to, :class_name => "Author", :foreign_key => "subscribed_to"
end

This way we can use

  1. some_author.subscribed_by_author -- the list of the authors to whom some_author is subscribed.
  2. For any subscription we can know both ends (who is subscribed to whom)

But the question is how to get the list of people subscribed to some author using only rails (not using plain SQL) i.e get the answer to :"Who is subscribed to some_author?"

Question: is there any ability in Rails to get the relationship working both sides i.e. not only writing some_author.subscribed_BY_author but having some_author_subscribed_TO_author? If there is one, then what is it?

P.S. Obvious solution is to

  1. Change the database design, adding a column named "direction"
  2. Create 2 records each time a subscription is created
  3. Add to the author model

    has_many :subscribed_BY_author, :through => :subscriptions, :source => :subscribed_to, :conditions => "direction = 'by'"

    has_many :subscribed_TO_author, :through => :subscriptions, :source => :subscribed_to, :conditions => "direction = 'to'"

But i wonder if there is a solution without changing the database design.


Solution

  • # Author model
    has_many :subscriptions_to, :class_name => "Subscription", :foreign_key => "subscribed_to"
    has_many :subscribed_to_author, :through => :subscriptions_to, :source => :author
    

    As far as I know - it works! :)