Model question

Hello,

I’m building a contact management system for a small school and I’m
having trouble coming up with the right data-model that works well with
Rails.

Here’s the mysql
contacts
id
first_name
last_name

donors
id
contact_id
status

faculty
id
contact_id
hire_date

directors
id
contact_id

The contact table contains basic stuff that all contacts will have, and
subsequent models extend Contact to add additional data. Some of the
sub models are Donor, Faculty, Director. A single Contact can be a
Donor, Faculty, and a Director, so it’s what I think is called
multi-table inheritance which isn’t really supported in Rails. Some of
the workarounds I’ve seen seemed overly complex.

I was planning to do something like this:
class Donor < ActiveRecord::Base
has_one :contact
end

for all the sub models and I think that would work, but I can’t use the
syntax Donor.find(1, :include => :contact) because this requires a
donor_id column in contacts, and I doesn’t seem to be good to have
donor_id, faculty_id, director_id, foreign key columns in contacts
(maybe this isn’t a problem??) Also, assuming the above, when I create
one of the sub models I can’t get the contact_id to save to donor
automatically.

@contact = Contact.new(params[:contact])
@donor = Donor.new(params[:donor])
Donor.transaction do
@contact.save
@donor.contact_id = @contact.id
if @donor.save
flash[:notice] = ‘Donor was successfully created.’
redirect_to :action => ‘list’
else
render :action => ‘new’
end
end

I can get the schema above to work, but I think I’ll have to do a lot
of custom sql to make it work rather than using the built-in :include.
Unless there’s a way to “reverse” the underlying join of the has_one
relationship so a contact_id in donors joins to contacts?

Thanks for the help!

Mark

Just reverse the relationship, as you suggested:

class Donor < ActiveRecord::Base
belongs_to :contact
end

class Contact < ActiveRecord::Base
has_one :donor
has_one :faculty
has_one :director
end

Rails will then use the contact_id field in Donor, Faculty etc. and you
won’t need the donor_id and other id fields in Contact.

Hello again,

I’ve been thinking about this some more and I think the easiest thing
to do is to put foreign key columns in contacts for each of the
sub-models. While this would require a schema change in contacts for
every “new” sub-model, that’s okay for this project.

By doing this, I’m not polluting the elegant natural language of rails.
The Donor can :has_one Contact, and I can do a Donor.find(1, :include
=> :contact).

The following code worked great:

c = Contact.new
d = Donor.new
d.contact = c
d.save
c.donor_id
=> 1

Donor.find(1, :include => :contact)

Regardless, I’m still looking forward to other ideas.

You’re completely correct, I learned that this would work after my
first post, but I was conflicted in saying that a Donor belongs to a
Contact, because it didn’t really describe the relationship. Anyway,
thanks!

You really shouldn’t get caught up with the descriptions “has_one” and
“belongs_to”. In rails they mean little more than “I don’t have the
foreign key” and “I do have the foreign key”. There’s no reason why the
item that “belongs to” another item needs to have the foreign key. And
rails will work quite happily either way. All the code you used in your
example will work identically if you reverse the relationship (except
for c.donor_id, obviously). In particular, the “:include => contact”
will still work with the relationship reversed.

I’ve used this “reverse relationship” in a couple of places in my
current project because it simply makes more sense that way. And rails
hasn’t complained once.

mveerman wrote:

You’re completely correct, I learned that this would work after my
first post, but I was conflicted in saying that a Donor belongs to a
Contact, because it didn’t really describe the relationship. Anyway,
thanks!

You could also look into either using Single Table Inheritence (STI) as
the fan out of the subclasses isn’t too high, or use a polymorphic
association.