This is part 5 of the series; part 4 is here.
On yesterday's blog entry I described an issue I found with the Rails ActiveRecord framework. Here is a summary:
Let's say that in my domain model I have a School and a Teacher, and both have an Address. Good database design suggests that you have separate tables for School, Teacher and Address, and that entries in the School and Teacher tables contain a key to their respective Address. Here's an article on IBM DeveloperWorks that says that same thing; see the section called "Complex Datatypes".
To recap, there's a one-to-one mapping between School and Address, a one-to-one mapping between Teacher and Address. The School and Teacher tables each contain a key to an Address.
In addition, I'd like there to be a dependency relationship between School and Address so that when a School is deleted, its associated Address is also automatically deleted. I'd also like the same dependency to exist between Teacher and Address.
Ruby on Rails includes a framework called ActiveRecord that directly supports access to relational databases without having to write any custom code. A scaffolding tool generates Ruby classes based on the names of your SQL tables, and the runtime system introspects SQL tables to add the appropriate accessors to the Ruby classes. The only thing you have to do is to "annotate" the Ruby classes with words like "has_one", "belongs_to" and "has_many" to indicate the relationships between the entities.
The current version of ActiveRecord requires that if model A has a dependency on model B, then class A must "belong_to" class B and the table for A must contain the key to B. In my case, since Address is dependent on School, it means that I'd have to put a key to the School into my Address table, which violates good database design principles.
Since I don't want to warp my database design to fit a current limitation of Rails, I added a few lines of custom code to my School and Teacher classes to implement the desired dependency. If you're interested, here's the code from School:
class School < ActiveRecord::Base
belongs_to :address # yuch
# use callback hook to implement dependency
before_destroy { |m| m.address.destroy if m.address }
# when new address is assigned, delete old one
def address=(new_address)
address.destroy if address
new_address.save if new_address
self[:address_id] = new_address.id
end
end
Clearly, this is not ideal. It would not be hard for Rails to directly support my common use case so that no custom code is needed.
Tomorrow I'm going to post some ideas on how to improve and simplify ActiveRecord.
Part 6 of this series is here.

Recent Comments