lost password?

home
•  xaraya
•  rails +
•  django
•  webdev
•  xamp
•  musings

rss
Tag this page
   

» Blogs that link here
last modified: Apr 29, 2007
(first posted: Apr 29, 2007)
(1535 Reads)
Permalink

When to use 'has one'

Why/when would you use has_one versus extend the model with more fields?

In designing my models I sometimes get confused if and when to break out fields into a separate model. I posted this question in the #rubonrails irc channel, here's some thoughts based on the comments.

Models (db tables) have attributes (columns). An attribute can be a string, integer, etc type. Or it can be a foreign-key, an ID of a row in another table.

In Rails, the child model has an item ID of its parent (it belongs_to the parent). And then you specify the relation in the parent model as either has_one or has_many.

Use has_many for 1:N relations

Sometimes the relations (and sematics) gets reversed, and become confusing. Consider a model (parent) that can have just one child, but the child can belong to multiple parents. (I'm intentionally reversing the Rails terminology here to make a point in plain english).

This is NOT a has_one relation, and you must reverse the sematics. The parent has a foreign ID to a child record, and the child has_many parents. The parent belongs_to the child.

For example, lets say our model of a PC says it has one operating system. But the OS can reside on many PC's. No, the PC does NOT "has_one" OS. Rather, the PC has a foreign ID of an OS item. Thus, the PC belongs_to the OS, and the OS has_many PC's.

When has_one isn't needed in 1:1

Apart from 1:N relations, what if the parent only has only one child and the child has only one parent, why bother modeling the child as a separate table? Why not just include those child fields in the parent model in the first place?

Let's say I have a User model, and want to add options for preferences like avatar, homepage, etc. Should these be in a separate UserPreferences model? In this case the additional attributes just extend the User model, and I'd just add these columns to the existing User model.

So when do you need a separate model and a has_one relation? It depends on what you're modeling, it's a way of keeping things that are conceptually separate separated.

Representing "optional" at a course level

Contrary to what I just said above, consider the case of a set of user preferences that are completely optional. It could be handy to move them into their own model. Then you can test for the presence of a UserPreferences record for that user, as needed. Then the UserPreferences (child) would have an user_id attribute (belongs_to user), and the User model has_one user_preference.

Treating the child as an object in itself

A more significant example is when you need to separately manage objects in child model. For example, lets say each User on your system can have only one credit card. Yes, you could extend the User model with credit card information.

But lets also say you have some backend administrative tools for processing, managing, and reporting on credit cards. In that context, the credit card is an object on its own and may be should be managed in a separate model. Each creditcard has a user_id attribute pointing back to the User record, and the User has_one creditcard.

Reusing the child model

Even if you don't need to track the child objects, there still can be alot of value in re-using the child model.

Let's say your Users can have only one address. But elsewhere in your app, other things have addresses, like Companies. Using a has_one relation lets you factor out the Address fields, and corresponding methods, DRYing up your code and models. Once you have an Address model, you can add useful stuff to it like formatting the strings, validations, testing, etc 

Thus, even though (presumably) no one shares a given Address record in your app (e.g. there's a unique 1:1 relation where each Address item is used only once), other parent models can share the db schema, model, and methods.



 

There are no comments attached to this item.

Post a new comment

: This is not spam

Name :