Thursday, December 6, 2007

Assigning values to has_many through relationships

I found out the hard way today that you can't directly assign values to a has_many :through relationship like you can with a has_and_belongs_to_many relationship. First let me explain the two approaches for many to many relationships if you're not sure what they are.

Say you have a "group", and groups can have any number of accounts. Accounts can also belong to any number of groups. You can accomplish this two ways. First, create a join table called accounts_groups, with no id, and two primary keys, account_id and group_id. Then simply put this one line in the group class
has_and_belongs_to_many :accounts

and this in the account class
has_and_belongs_to_many :groups

The downside of this approach is that you can't have additional information about the association (like number_of_logins, etc.). If you don't need additional information about the association, I think has_and_belongs_to_many (habtm) is the better approach (less code, less complicated), even though this seems to have fallen out of favor in the Rails community.

If you do need information in the association, or you just want to use the Rails community's preferred approach, first create a join table named memberships, with a primary key id, account_id, and group_id. Then add a class named Membership:
class Membership < ActiveRecord::Base
belongs_to :account
belongs_to :node
end

Add this to your group class:
has_many :memberships
has_many :accounts, :through => :memberships

And this is your account class:
has_many :memberships
has_many :groups, :through => :memberships

One downside of has_many :through is that you can't assign directly to the relationship. For the above example, say you had an array of accounts that you want to assign to a group. You can't code
@group.accounts = array

But you can with habtm You can also assign an array of id's to a has_and_belongs_to_many relationship by using relationship_ids = array. Note that it's the singular form of the relationship in the name of the function. One more reason to use habtm.

BUT I found a really good function to allow you to assign values to has_many through relationships at the Grinding Rails blog (http://tuples.us/2007/05/09/metaprogramming-has_many-through-accessors/). It's really useful when using has_many through relationships.

No comments: