Re: Extending Group for a GroupProfile

156 views
Skip to first unread message
Message has been deleted

Malcolm Tredinnick

unread,
Jan 8, 2009, 11:09:03 PM1/8/09
to django...@googlegroups.com
On Thu, 2009-01-08 at 19:46 -0800, Cortland Klein wrote:
> Hi. I'm planning on extending Group for a GroupProfile. I noticed that
> the recommended way to extend User is to create a UserProfile with a
> ForeignKey(User, unique=True) instead of a OneToOneField[a].
>
> 1. Why is it recommended to use a ForeignKey(unique=True) from a
> UserProfile to User instead of a OneToOneField or subclassing User for
> multi-table inheritance?

Partly historical. Prior to the queryset-refactor branch being merged
into trunk, we weren't recommending that people used OneToOneField, as
it had a few internal problems. That was subsequently tidied up and
OneToOneField is a grade A field again these days.

Similarly for model inheritance (although see the next paragraph for the
real problem here). It was only added to Django last year, but the user
profile concept has been around for ages. There's also an element of
explicitness involved: using the related field and including it
explicitly makes it clear what's going on. Model inheritance is, in many
ways, a bit of a cheat to make something look Pythonic that really isn't
(the relational database structure of the storage leaks through in a few
ways).

More importantly, though, just as in Python you cannot "downcast" with
Django's model inheritance. That is, if you've already created the User
instance, you cannot, without poking about under the covers, make that
instance correspond to a subclass instance that you haven't created yet.
Since user profiles are always created after the User instance (possibly
via the post_save signal, but that's the earliest you can do it), this
is a bit of a problem. On the other hand, it's easy and documented how
to add a user profile class at a later stage after the user is created
(you can simulate "downcasting", but you end up having to admit that the
multi-table inheritance is done with a OneToOneField and poking at a
hidden field and possibly having to sacrifice a chicken, so you might as
well just right it out explicitly in the first place).

> 2. As I'm planning on extending Group with GroupProfile, should I use
> a ForeignKey(Group, unique=True), a OneToOneField(Group), or have
> GroupProfile subclass Group to use Multi-table interitence? Does it
> even matter since Group currently has no get_profile() function anyways?

These days, if you're implementing something that really is one-to-one,
I would recommend the OneToOneField. In fact, I suspect there's probably
no reason it can't be used for the User profile class, either. The only
significant difference between OneToOneField and ForeignKey(unique=True)
is when you're traversing the reverse relation. If you have

class ModelA(models.Model):
...

class ModelB(models.Model):
related = Models.OneToOneField(ModelA)
...

and obj_a is a ModelA instance, then obj_a.modelb will return a ModelB
instance.

On the other hand, if ModelB is using a ForeignKey(ModelA, unique=True)
setup, obj_a.modelb_set.all() is the required spelling and will return
an iterator containing a single ModelB instance. So, for single instance
things, OneToOneField provides a more natural API -- you immediately get
hold of the object you care about.

Note, however (in case you care) that User.get_profile() doesn't use the
reverse relation, which is why you can switch OneToOneField and
ForeignKey there. It does an explicit get() call to retrieve the
instance with the correct primary key.

Regards,
Malcolm


Reply all
Reply to author
Forward
0 new messages