Two-to-one mappings

Hey all,

I have a pretty simple question, but I'm not sure of a good solution
to it. Essentially, I want to provide a two-to-one mapping of models.
I'm working on an application for a contest, where every (unordered)
combination of two Rounds is supposed to be assigned to one Room. Any
Room might have many different combinations of Rounds, however.

What is the Right Way of doing this in Rails? Maybe create a model
that holds the associated foreign keys? Also, what would be a good
way to scale this out, if I wanted to be able to map unordered
n-element collections of Rounds to obtain a Room?



Imagining that every round must belong to a room, here's a simple and
straightforward implementation:

class Room < ActiveRecord::Base
  # this class needs a max_rounds and rounds_count integer columns
  has_many :rounds

  def accepts_more_rounds?
    max_rounds < rounds_count


class Round < ActiveRecord::Base
  # this class needs a room_id integer column
  belongs_to :room, :counter_cache => true

  validate_on_create :validate_rounds_count


  def validate_rounds_count
      errors.add( :room, "has already met it's rounds limit" )


room = Room.create( :max_rounds => 2 )
round_1 = room.rounds.create( :name => 'round 1' )
round_2 = room.rounds.create( :name => 'round 2' )
round_3 = room.rounds.create( :name => 'round 3' ) # this one isn't
going to be created
round_3.new_record? == true

That's a clean solution, however I don't know if it satisfies the fact
that "any Room might have many different combinations of Rounds"

Not sure I understand correctly, but if a room can have many
combination of rounds, and each combination of rounds has more than
one round, you could try this:

Room and Round models I assume you already have. A room can have many
round_combinations (create the RoundCombination model with a room_id.
Room :has_many :room_combinations, and
RoomCombination :belongs_to :room). Then create a join table between
round_combinations and rounds (HABTM).

You can use callbacks or validation to limit the relationship to two
rounds maximum.

Cool! Harold, your solution strikes me as being exactly the way to do
it. I've implemented it, and things seem to be sailing smoothly.
Thanks a lot to both of you.



Great! Glad it worked.

Ah, so one follow up question: each Competitor in the competition is
signed up for two rounds, and needs to know which room he or she is
assigned. This is a has_one relationship; however, I'm not quite sure
of the right way to map it. Thoughts?


Does a user have many round_combinations? and a round_combination has
many users? Seems like it. If so, first thought is to create another
HABTM table between round_combinations and users. Then
@user.round_combinations.each { |rc| } gives you the rooms...

Actually, by user I mean competitor, but you get the point…

Ah, sorry, my specification was not very good. The Big Picture here
is that we assign each Competitor to a room based on the particular
combination of Rounds he or she is signed up for. So for example, we
could have

r = = room_1
r.rounds = [round_1, round_2]

Now if c is a Competitor who is signed up for round_1 and round_2, I
would like to be able to do
#=> room_1

So essentially, a HABTM table does not seem to be the right way of
capturing this relationship. A Competitor "knows" its
RoundCombination because the Competitor is signed up for a particular
combination of Rounds. I've managed a provisional, rather inefficient
implementation, of as

class Competitor < ActiveRecord::Base
  has_many :rounds

  def room
    RoundCombination.all.each do |rc|
      return if rc.rounds.sort_by {|r|} == rounds.sort_by

I feel this should be done with some SQL joins or something. Does
Rails provide a relationship to manage this?

Thanks again,


From what I understand, one competitor will have as many rooms as RoundCombinations. Your room definition below would return the first one it finds.

I think you can do something like:

@competitor.rounds.each { |round| }

Would return an array of rooms for each of @competitor’s rounds.

That sound right?

Not quite, unfortunately. The point here (that is making things
complex), is that


has no semantic. Rather, the semantic is more like

[round_1, round_2].round_combination

That is, an (unordered) set of two rounds determines the

On the flip side, a Competitor has one Room. As motivation, what's
going on here is that Competitors are competing in a two-round math
competition. They each sign up for two rounds ahead of time, and we
put them in a room determined by which two rounds they signed up for.
(Hence, a Competitor also has one RoundCombination; it's just not
clear to me how the join query should work... I'm starting to think
maybe I should just write that by hand.) In any case, the code I
provided has the right functionality, but it's bad in the sense that
it loads all of the RoundCombinations as AR objects each time its
called, which is a lot of overhead...