Search code examples
ruby-on-rails-3relationships

Joining 3 tables in Ruby on Rails 3


I am trying to extract all Posts for a given User in the bellow relationships. Not sure whether I got them right, so I'll better explain. A User has Ownerships and Memberships in some Groups. A User can be either a Member or an Owner of the Group, but not both. Every Post has an id of the user and of the group. I think the problem is due to the relationships noted below. How can I get around it? One more thing. I have to also find all posts that were posted by other users in the user's groups. In other words, I have to pass through groups.

       /-- Owner ---\
User --              -- Group -- Post
  |    \-- Member --/             |
  |_______________________________|

class User < ActiveRecord::Base
  has_many :posts, :dependent => :destroy
  has_many :ownerships, :foreign_key => "user_id", :dependent => :destroy
  has_many :memberships, :foreign_key => "user_id", :dependent => :destroy

  # Problem with these two? I think so.
  has_many :groups, :through => :memberships, :source => :user
  has_many :groups, :through => :ownerships, :source => :user

class Ownership < ActiveRecord::Base
  belongs_to :users, :class_name => "User"
  belongs_to :groups, :class_name => "Group"
  has_many :posts, :through => :groups, :source => :posts

class Membership < ActiveRecord::Base
  belongs_to :users, :class_name => "User"
  belongs_to :groups, :class_name => "Group"
  has_many :posts, :through => :groups, :source => :posts

class Group < ActiveRecord::Base
  has_many :posts, :dependent => :destroy

class Post < ActiveRecord::Base
  belongs_to :user
  belongs_to :groups

The errors is coming from the line:

_groups = user.groups

The error as following:

Could not find the source association(s) :user in model Ownership. Try 'has_many :groups, :through => :ownerships, :source => '. Is it one of :users, :groups, or :postings?


Solution

  • First up: you're getting that error you're seeing because you've defined the associations in the Membership and Ownership table as this:

    belongs_to :users
    

    When they should belong to only one user, i.e. singular user:

    belongs_to :user
    

    But even then you will run into problems!

    I think having a Membership model and an Ownership model are what will trip you up next. I don't understand what the purpose of having an Ownership model provides, other than signifying ownership of a group, which could be done by a field on the memberships table's records called owner for instance. It's over-engineering.

    The problem with the Rails code you've got there is that you're defining that you have many posts through one association and then you're telling it that you have many posts through another association. In effect, you're doing this:

     def posts
       # find posts for the groups that I own
     end
    
     def posts
       # find posts for the groups I belong to
     end
    

    It is not mistake here that there are two identically-named methods. This is exactly what you are doing by defining two has_many associations with the same name.

    So hopefully now you can see why having an Ownership and a Membership model is the path to madness.

    I would really recommend that you just have a Membership model that has a boolean attribute declaring an owner for a group. This would also mean that, if you wanted to, you could have new owners for a group in a very easy fashion: just flip the boolean. No need to create another record in another table.

    One Membership model to rule them all.