Associating 2 Users in 1 modeled join table

I’m having trouble getting this right (ActiveRecord associations are my
weakness). Given two models:

Racers:
:racer_id
:name

TandemRacers:
:racer_1_id
:racer_2_id
:start (datetime)
:return (datetime)

How do I associate the models such that I can call a Riders coriders,
and also see what riders have ridden on a tandem? Not all Racers will
ride tandem, and those that do won’t ride them for the whole race.
TandemRacers will have it’s own controller.

So far I have:
class Racer < ActiveRecord::Base
has_and_belongs_to_many :coracers,
:class_name => “Racer”,
:join_table => “tandem_racers”,
:association_foreign_key => “racer_2_id”,
:foreign_key => “racer_1_id”
end

I’m not sure what should in the TandemRacers model on the other end
though.

Chris B. wrote:

:start (datetime)
:class_name => “Racer”,
:join_table => “tandem_racers”,
:association_foreign_key => “racer_2_id”,
:foreign_key => “racer_1_id”
end

I’m not sure what should in the TandemRacers model on the other end
though.

Is there really an “ordering” to the pairings. E.g. what does it mean
for a racer to be racer_1 or racer_2?


Michael W.

Michael W. wrote:

Is there really an “ordering” to the pairings. E.g. what does it mean
for a racer to be racer_1 or racer_2?

No, there is no ordering. A tandem bike has two riders. It doesn’t
matter what order. I could have called it front_racer, back_racer, etc.
Ordering is not important, only the pairing.

Chris B. wrote:

Michael W. wrote:

Is there really an “ordering” to the pairings. E.g. what does it mean
for a racer to be racer_1 or racer_2?

No, there is no ordering. A tandem bike has two riders. It doesn’t
matter what order. I could have called it front_racer, back_racer, etc.
Ordering is not important, only the pairing.

The problem that I’m facing is that I will need to be able to know:

  1. If a rider was part of a tandem team, and if so how many times
  2. The riders s/he was partnered with.

However, this has to be done regardless of whether they were rider A or
rider B. I don’t think a simple link table will work as it would require
that for every pairing I have to enter 2 rows: A -> B and B -> A.
Perhaps there is a way to do it using the :conditions or :finder_sql
options of the association, but I’m not sure where to begin with those.

Suggestions are appreciated :slight_smile:

From: [email protected]
[mailto:[email protected]] On Behalf Of Chris B.

The problem that I’m facing is that I will need to be able to know:

  1. If a rider was part of a tandem team, and if so how many times
  2. The riders s/he was partnered with.<

Would it help to introduce a Bike model? Some Bikes have two Riders;
some
have one.

///ark

Mark W. wrote:

From: [email protected]
[mailto:[email protected]] On Behalf Of Chris B.

The problem that I’m facing is that I will need to be able to know:

  1. If a rider was part of a tandem team, and if so how many times
  2. The riders s/he was partnered with.<

Would it help to introduce a Bike model? Some Bikes have two Riders;
some
have one.

///ark

I’m not sure that would help. I’m assuming that all racers will have and
use a one-rider bike. However, part of the race involves the option of
using a tandem bike for extra points.

So, it’s assumed from the Racers table that they each have a one-rider
bike. And there is only one tandem bike that will be shared - two riders
at a time - and that’s what I am most interested in tracking - what two
riders were on the bike. I suppose that it is less important to know who
was paired with who, but I would like to know who rode the tandem bike
at any given time, and perhaps how many times (though this will probably
be 0 or 1)

On 10/4/07, Chris B. [email protected] wrote:

The problem that I’m facing is that I will need to be able to know:

  1. If a rider was part of a tandem team, and if so how many times
  2. The riders s/he was partnered with.

However, this has to be done regardless of whether they were rider A or
rider B. I don’t think a simple link table will work as it would require
that for every pairing I have to enter 2 rows: A → B and B → A.
Perhaps there is a way to do it using the :conditions or :finder_sql
options of the association, but I’m not sure where to begin with those.

Seems to me you need a model for a Rider, and another model for an
“Event”. The “Event” would represent a specific instance of a bike
running a race, so it would have dates, times, etc.

Since an “Event” (not sure that’s the best name) could have 1 or 2
riders, and any given rider could participate in multiple “Events”, it
looks like a habtm relationship to me.

riders ← events_riders → events

Bob S. wrote:

Seems to me you need a model for a Rider, and another model for an
“Event”. The “Event” would represent a specific instance of a bike
running a race, so it would have dates, times, etc.

Since an “Event” (not sure that’s the best name) could have 1 or 2
riders, and any given rider could participate in multiple “Events”, it
looks like a habtm relationship to me.

riders <-- events_riders --> events

I understand that logic, but I’m not sure that’s an appropriate solution
for this reason:

This is a one-off application for a one-off race. No other racing events
need to be tracked. Even if you consider an event to just be the fact
that a racer is riding a bike in the race, they will always be riding at
least one bike by virtue of signing up for the race, so that seems a bit
redundant. These racers will typically be riding their own one-rider
bikes but may, optionally, over the course of the single race, ride a
tandem bike with another rider - It’s a silly race, what can I say…

The other problem that I see with the suggested model is that it still
doesn’t tell me who rode with whom. But I guess I did say that was less
important, didn’t I? :slight_smile:

Really what I want from this is the ability to say Rider.CoRiders and
get back Rider B, C and D, etc. Or to say Rider.tandem_rides.count, etc.

If all else fails, I can always fall back to straight SQL and not worry
about the mapping (this tool will be used for all of 2 hours, and could
probably be done in Excel much easier, but looked like a good place to
stretch my Rails muscles)

Thanks for the proposed solutions. I appreciate the thought you’ve put
into this.

On 10/4/07, Chris B. [email protected] wrote:

riders ← events_riders → events
tandem bike with another rider - It’s a silly race, what can I say…
probably be done in Excel much easier, but looked like a good place to
stretch my Rails muscles)

Thanks for the proposed solutions. I appreciate the thought you’ve put
into this.

I’m thinking something like:

class TandemRide
has_and_belongs_to_many :riders

work out how to constrain/validate this to only two

end

class Rider < ActiveRecord::Base

has_and_belongs_to_many :tandem_rides

Theres probably a better way to do this with sql, but if there

aren’t too many riders

and rides this would probably work

def co_riders_with_duplicates
tandem_rides.inject([]) {|res, p| res << p.riders - [self])
end

def co_riders
co_riders_with_dupicates.uniq
end
end

Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Rick Denatale wrote:

I’m thinking something like:

class TandemRide
has_and_belongs_to_many :riders

work out how to constrain/validate this to only two

end

class Rider < ActiveRecord::Base

has_and_belongs_to_many :tandem_rides

Theres probably a better way to do this with sql, but if there

aren’t too many riders

and rides this would probably work

def co_riders_with_duplicates
tandem_rides.inject([]) {|res, p| res << p.riders - [self])
end

def co_riders
co_riders_with_dupicates.uniq
end
end

Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Hmm, that looks interesting. But does that put me back to having two
entries in tandemride for each pairing, A → B and B → A? If so, so be
it. That’s easy enough to do and build constraints for.

Also, what does the “- [self]” part of the inject block do?

Thanks for the idea!

Rick Denatale wrote:

the tandemride table really only needs an id field, at least with the
requirements you’ve given.

You’ll need a riders_tandem_rides table to represent the habtm
relationships

Ah, OK. I’ll try that. Thanks!

On 10/4/07, Chris B. [email protected] wrote:

class Rider < ActiveRecord::Base
def co_riders
entries in tandemride for each pairing, A → B and B → A? If so, so be
it. That’s easy enough to do and build constraints for.

the tandemride table really only needs an id field, at least with the
requirements you’ve given.

You’ll need a riders_tandem_rides table to represent the habtm
relationships

Now lets say we had the following tandem rides

Joe and Sally
Joe and Sam
Sally and Sam

riders table
id Name …
1 Joe
2 Sally
3 Sam

tandem_rides table
id
1
2
3

iders_tandem_rides table
rider_id tandem_ride_id
1 1
1 2
2 1
2 3
3 2
3 3

Also, what does the “- [self]” part of the inject block do?

It removes the rider himself from the list of his co_riders.

Mind you, none of this is tested code.

Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Rick Denatale wrote:

I’m thinking something like:

class TandemRide
has_and_belongs_to_many :riders

work out how to constrain/validate this to only two

end

class Rider < ActiveRecord::Base

has_and_belongs_to_many :tandem_rides

Theres probably a better way to do this with sql, but if there

aren’t too many riders

and rides this would probably work

def co_riders_with_duplicates
tandem_rides.inject([]) {|res, p| res << p.riders - [self])
end

def co_riders
co_riders_with_dupicates.uniq
end
end

Hi Rick,

I had to make some changes to get it to work, but it works! Thanks!!

Here’s what I ended up with:

class Racer < ActiveRecord::Base
has_and_belongs_to_many :tandem_rides, :class_name => “TandemRide”

Theres probably a better way to do this with sql, but if there

aren’t too many riders and rides this would probably work

def co_riders_with_duplicates
tandem_rides.inject([]) { |res, p| res << p.riders - [self] }
end

def co_riders
co_riders_with_duplicates.flatten.uniq
end
end

class TandemRide < ActiveRecord::Base
has_and_belongs_to_many :riders, :class_name => “Racer”
end

So now, this works:

t = TandemRide.create
=> #TandemRide:0x492bef0

t.riders << Racer.find(2)
=> [#Racer:0x490af34]

t.riders << Racer.find(3)
=> [#Racer:0x490af34, #Racer:0x4903b80]

r = Racer.find(2)
=> #Racer:0x48fbac0

r.tandem_rides
=> [#TandemRide:0x48f8fb4]

r.co_riders
=> [#Racer:0x48f6458, #Racer:0x48f4a18]

Thanks again!!