Has_Many :Through - Setting a type on the join table

Apr 21, 2009 06:24PM

fancy dancy many-to-many model associations!

I love Ruby! I've always loved Ruby, but today's discovery just reaffirms the validity of my devotion.

I may need to watch my words in the future. My girlfriend (like all girlfriends) doesn't like competition, and I wouldn't want any harm to come to my beloved Ruby.

This handy little trick was picked up from Josh Susser over at has_many :through. His post Magic join model creation goes over a lot of the details, but needed a little updating for Ruby 1.9.1 and Rails 2.0.

We've all run across the situation: two tables, many to many association, but one model can have many 'types' of another.

To better explain what I'm getting at take this example:

People <-n --- n-> Book

A person has many books, a book has many people. Simple enough, right? Well, sort of. I'm sure those people associated with a book can have any number of affiliations that we may want to capture in our data model.

Author <-n --- n-> Book
Editor <-n --- n-> Book
Reviewer <-n --- n-> Book

All of these associations (author, editor, reviewer) are people, and all are associated with our book in some way or another, but creating the individual tables (book_authors, book_editors, book_reviewers) isn't my ideal solution to this problem, and there's a better way.

Instead of creating all of those join tables, lets just make one: "book_people".

create_table :book_people do |t|
    t.column :person_id, :integer
    t.column :book_id, :integer
    t.column :person_type, :string
    t.timestamps
end

In model "People".

class Person < ActiveRecord::Base
    has_many :book_people, :dependent => :destroy
    has_many :books, :through => :book_people
end

In model "Book"

class Book < ActiveRecord::Base
    has_many :book_people, :dependent => :destroy
    has_many :people, :through => :book_people, :uniq => true

    has_many :authors, :through => :book_people, :source => :author, :conditions => ["`book_people`.contact_type = ?","Author"] do
        def <<(author)
            BookPeople.send(:with_scope, :create => { :contact_type => "Author" } ) { self.concat author }
        end
    end

    has_many :editors, :through => :book_people, :source => :editor, :conditions => ["`book_people`.contact_type = ?","Editor"] do
        def <<(editor)
            BookPeople.send(:with_scope, :create => { :contact_type => "Editor" } ) { self.concat editor }
        end
    end
end

And finally, in model "BookPeople"

class BookPeople < ActiveRecord::Base
    belongs_to :person
    belongs_to :book

    belongs_to :author, :class_name => "Person", :foreign_key => :person_id
    belongs_to :editor, :class_name => "Person", :foreign_key => :person_id
end

Amazing! Three tables, infinite association types! This model is superior to the many association tables model in a number of ways:

  1. If you simply want to find out all books a person is associated with, regardless of granularity, person.books works nicely! One table, one query.
  2. The Person model can be extended. Associations such as person.authored_books, person.edited_books etc. are easy to add.
  3. Clean database - I always try to get away with as few tables as possible.

Now, I just hope this code works properly.

-Butch

    Name:
    Alexis M.
    Website:
Nov 25, 2010 05:03AM
You still running this site? I was wondering if you could give me an update on your post. The code you posted doesn't work. I've cleaned up some of the typos in it, but sitll no go. Care to take a look?
    Name:
    Butch Marshall
    Website:
Dec 03, 2010 04:07AM
Hi Alexis, yes I still check in from time to time. I haven't made a post in awhile but I hope, one day, to post regularly. I actually wrote this while riding on a train - so I'm not surprised by any typos. Since actual interest has been shown - I'll try and post a simple working project.
Dec 18, 2010 11:20AM
Did you downloaded Wikileaks docs? Give me link plz Thank for all
Jan 05, 2011 11:14PM
google best google sites
Jan 25, 2011 03:05AM
Blithesome Reborn Year everybody under the sun! :)
Jan 27, 2011 07:58PM
???? ???? ? ???????? ????????????????? ???????? pr110 ??????????? ???????? ????? ?????? ??????? ? ???????????????? ?????? ??????????? ???????? ? ????????? ?????? ?????? profit?????? ?? ???? ??????? ??????????? ???????? ????????? ??????? ???????? ??????? ???????????????? ???????? ????????????????? ????? ??? ?????? ??????????? ???????? ????? ?????? ??????? ?????? ????? ?????????????? ???????? ????? ???????????? ???? ???? ? ???????? ??????????? ???????? ? ????????? ?????? ?????? ?? ??????????????? ? ??????? ?????????-??????? ?????????????? ?? ???????? ????? ????? ?????? ??????????? ????????
Jan 28, 2011 11:25PM
Happy New Year harry! :)