views:

214

answers:

2

I have a typical User model (username, password, name, etc). I want to allow users to attach three chosen categories to their account. Each of the three categories exist in the Category model.

How can I link three foreign keys from Category to a single User without using an intermediate table for tracking? Each Category can belong to any number of Users, but each user can have only three Categories.

I played with has_many :through but I really don't think a relationship table is necessary and it would generate a lot of coding on my end to work with it.

Any ideas?

+2  A: 

From a code maintainability standpoint, even though you may want to restrict the number of categories a user can pick to 3 right now, you may not want to code it with this limitation. You'll be kicking yourself later when you want to increase it to 5 or reduce it to 1. My suggestion would be to just use has_and_belongs_to_many with a join table (you don't need :through because, from what I can tell, you don't need a join model, just a join table). Using HABTM will automatically use a join table so you don't have to worry about writing the code to handle that. Just make sure you name the join table and its columns properly.

As for actually restricting the user to only 3 categories, just implement that restriction in the view/controller (i.e. restrict the UI so they can't choose more than 3).

I'm sure you've already read this, but in case you haven't, here's the docs for HABTM.

Marc W
Followed your advice and it ended up working very well and very cleanly. Thanks! I guess the core issue was my understanding of has_and_belongs_to_many and the join table.
Retro486
HABTM scared me at first since it seems so overly complicated. But once you get used to it and what it's good for, it becomes clear how to wield its power properly. Glad I could help. Ian's idea for a model-level validation is also a good one to follow.
Marc W
+1  A: 

HABTM is your best bet. To restrict users to three categories, add a model level validation:

class User < ActiveRecord::Base
  has_and_belongs_to_many :categories
  validate :no_more_than_three_categories
protected
  def no_more_than_three_categories
    self.errors.add(:categories, "may not have more than three") if categories.size > 3
  end
end

At your discretion, pull out the magic number 3 to a class level constant or a configuration setting.

And don't fear the code. Do it right and the code will fear you.

Ian Terrell
Thanks for the bonus material for keeping the validation in the model (which I very much prefer). I'm still (and always will be) learning.
Retro486