Headshot-color me@jbrains.ca Find out where I'm appearing

Just To Be Clear: Rails and customized model relationships

This is the beginning of a new pseudo-column, titled Just To Be Clear. I intend to write about things I learn after either misunderstanding or finding unclear the existing documentation on the subject. In many cases, I simply don’t read carefully enough, but in general, I try to make as few assumptions as possible, and when that happens, sometimes I find gaps in either the literature or in product documentation. When I find those things, I will share them with you, partly as Google bait for the next poor soul who goes down the same path I do.

The first instalment of Just To Be Clear has to do with customizing model relationships in Rails. As you might know, you can easily express a parent-child relationship by adding foreign keys to the underlying database tables, then the appropriate use of :has_one, :has_many and :belongs_to. (While there’s more to it than that, I hope that’s enough explanation for now.) If you follow the naming conventions, you can get away with just doing this, for example:

class Order < ActiveRecord::Base
  belongs_to :person
end

In your database, you’ll have a table named orders with a foreign key named person_id that refers to the people table’s id column. But the person to whom the order belongs isn’t just any old person, but rather the payer. As a result, I’d rather call this person the payer in my code. Consulting Agile Web Development with Rails, I find I can do this:

class LineItem < ActiveRecord::Base
  belongs_to :paid_order,
             :class_name => "Order",
             :foreign_key => "order_id",
             :conditions => "paid_on is not null"
end

That seems simple enough, but I want to name my foreign key payer_id, so Rails will probably understand that an order’s payer should correspond to the column payer_id. It does everything else magically, so why not? I tried this:

class Order < ActiveRecord::Base
  belongs_to :payer, :class_name => "Person"
end

Well, it didn’t work. After 15 minutes of playing around in the Rails console, I noticed that my order objects had both a payer_id and a person_id, so while the payer attribute was correctly assigned, the payer’s id was assigned to person_id, and not payer_id. When I tried to save my order, the database would understandably complain about my trying to save a NULL value in the payer_id column.

So, just to be clear, Rails’ magic does not extend to knowing that the relationship :payer corresponds to a foreign key column payer_id; instead, it assumes the foreign key column name follows the model class name convention, which in this case is person_id. If you want the behavior I want, you need to specify the :foreign_key option.

July 01, 2006 20:20 rails, ruby, testing, just to be clear, article
blog comments powered by Disqus