Why does get_profile exist?

47 views
Skip to first unread message

Glenn Maynard

unread,
Apr 13, 2009, 4:30:15 PM4/13/09
to django-d...@googlegroups.com
Why do get_profile() and AUTH_PROFILE_MODULE exist, instead of just
declaring the Profile to User relationship as OneToOne and using the
auto-generated User.profile relationship?

I just changed my Profile's User relationship from ForeignKey to
OneToOne (the docs say to use ForeignKey, not OneToOne--is this an
error?) and it works fine.

It does mean you can access the profile without knowing the name of
the class, but you need to know the contents of the model to do
anything with it anyway. Do user profiles predate reverse
relationships? I'm just curious about the the design rationale here.

--
Glenn Maynard

Adi Sieker

unread,
Apr 13, 2009, 4:50:00 PM4/13/09
to django-d...@googlegroups.com

On 13.04.2009, at 22:30, Glenn Maynard wrote:

>
> Why do get_profile() and AUTH_PROFILE_MODULE exist, instead of just
> declaring the Profile to User relationship as OneToOne and using the
> auto-generated User.profile relationship?

Probably because third party apps can then get the user profile and
don't have to rely on the profile being called profile or whatever.

adi

Adi Sieker

unread,
Apr 13, 2009, 5:08:08 PM4/13/09
to django-d...@googlegroups.com
Hi,

On 13.04.2009, at 22:30, Glenn Maynard wrote:

oh, and this list is for the development of django.
Question about the usage of django should be directed at django-users.

adi

Glenn Maynard

unread,
Apr 13, 2009, 5:26:38 PM4/13/09
to django-d...@googlegroups.com
On Mon, Apr 13, 2009 at 4:50 PM, Adi Sieker <a...@sieker.info> wrote:
>> Why do get_profile() and AUTH_PROFILE_MODULE exist, instead of just
>> declaring the Profile to User relationship as OneToOne and using the
>> auto-generated User.profile relationship?
>
> Probably because third party apps can then get the user profile and
> don't have to rely on the profile being called profile or whatever.

Third party apps would need columns in it to use it, so apps would
need to require you to manually paste fields into your profile
model--which I assumed wasn't the intent.

> oh, and this list is for the development of django.
> Question about the usage of django should be directed at django-users.

I'm well aware of both lists. This is a question about the design
(development) rationale of Django.

--
Glenn Maynard

Waylan Limberg

unread,
Apr 13, 2009, 6:01:58 PM4/13/09
to django-d...@googlegroups.com
On Mon, Apr 13, 2009 at 4:30 PM, Glenn Maynard <glennf...@gmail.com> wrote:
>
> Why do get_profile() and AUTH_PROFILE_MODULE exist, instead of just
> declaring the Profile to User relationship as OneToOne and using the
> auto-generated User.profile relationship?
>

Well, prior to qs-refactor (just before 1.0), OneToOnes had some
issues and the documentation included very strong warnings that they
should not be used at all. That being the case, as that time you found
almost no use of OneToOne relationships within the community, let
alone Django itself. So, at least in part, the answer is for
historical reasons.

See other reasons discussed elsewhere [1]. Particularly the last
section of that post.

[1]: http://www.b-list.org/weblog/2006/jun/06/django-tips-extending-user-model/

--
----
\X/ /-\ `/ |_ /-\ |\|
Waylan Limberg

Glenn Maynard

unread,
Apr 13, 2009, 7:12:50 PM4/13/09
to django-d...@googlegroups.com
On Mon, Apr 13, 2009 at 6:01 PM, Waylan Limberg <way...@gmail.com> wrote:
> Well, prior to qs-refactor (just before 1.0), OneToOnes had some
> issues and the documentation included very strong warnings that they
> should not be used at all. That being the case, as that time you found
> almost no use of OneToOne relationships within the community, let
> alone Django itself. So, at least in part, the answer is for
> historical reasons.

I still see some issues with OneToOne reverse relationships. In
particular, it's easy to accidentally assign to them, which doesn't
work as one would expect--you have to save the model that holds the
relationship to save the change, so this leads to silent faliures:

obj.related = obj2
obj.save() # should be obj2.save()
obj.related = obj3
obj.save() # should be obj2.save(); obj3.save() in that order
obj.related = None
obj.save() # should be obj3.save()

I assume these are known limitations that are probably a bag of worms
to fix, but the main issue I have is that it leads to obscure
failures: these reverse relationships look the same as a forward
relationship, so I need to carefully examine the object to see if
"related" is a forward or reverse relationship whenever I assign to
it.

It would be useful if I could specify that I never want to be assign
to a reverse relationship and an exception should be thrown--any time
I'm doing that, it's probably a mistake.

> See other reasons discussed elsewhere [1]. Particularly the last
> section of that post.
>
> [1]: http://www.b-list.org/weblog/2006/jun/06/django-tips-extending-user-model/

> It’s a completely consistent generic interface. Using the standard API in the example above means hard-coding u.userprofile all over the place; what happens if you later change the name of that model, or decide you need to reuse that code somewhere else? Using the AUTH_PROFILE_MODULE setting and get_profile() makes your code more robust and more portable.

The same argument could be made for every relationship, both forward
and backward, and the result would be wrapping every relationship in a
function.

> It makes site-specific user customization insanely easy. If you’re using Django’s bundled ‘sites’ application to manage multiple sites which each have their own settings files, each one can use a different custom model tailored to its needs.

If they're different models, then they presumably contain different
things. I don't know what the benefit is of having a consistent
method name to access inconsistent models.

Anyhow, I'm not advocating changing it--nothing prevents people from
ignoring get_profile entirely and just using OneToOne (which is
probably what I'll do).

--
Glenn Maynard

James Bennett

unread,
Apr 13, 2009, 8:02:15 PM4/13/09
to django-d...@googlegroups.com
On Mon, Apr 13, 2009 at 6:12 PM, Glenn Maynard <glennf...@gmail.com> wrote:
> Anyhow, I'm not advocating changing it--nothing prevents people from
> ignoring get_profile entirely and just using OneToOne (which is
> probably what I'll do).

Well.

First let's explore why get_profile() exists. Back in the day -- the
first couple public releases of Django, back in 2005 and 2006 -- all
relationship-traversal in the ORM *was* actually done by explicit
method calls. For example, if you had a weblog with an Entry model,
and Entry had a foreign key to User, with the field named 'author',
you'd write 'some_entry.get_author()' to retrieve the related User
object. So a 'get_<something>()' method was what you'd expect to find
at the time.

The reverse side of the relationship was a bit trickier. If you had a
User and wanted all their blog entries, then (assuming the blog app
was named 'blog'), you'd write 'some_user.get_blog_entry_list()'. In
other words, the name of the application was a required part of the
reverse query syntax. And that gives a clue as to why get_profile()
was a useful abstraction: it required no knowledge of the name of the
application in which the profile model was defined, which meant you
could write more generic profile-oriented code.

Django's ORM was significantly refactored for the 0.95 release (what
was known as the "magic removal" effort), and relationships on models
(in fact, all fields on models, but that's not relevant to this
discussion) were encapsulated in descriptors, so that -- to the end
user -- they behaved more like normal attributes. At that point
get_profile() could have been refactored into a read-only property,
but there really weren't any pressing API-design reasons for doing so.

So get_profile() continues to exist, and to work much as it always has.

Personally, I think the machinery associated with it is still fairly
useful; as it stands, you can check for the AUTH_PROFILE_MODULE
setting to find out

1) Whether you're running on a project that has some sort of custom
user profile, and
2) If so, which model represents that profile.

That means you can write generic code which, for example, looks up the
profile model and generates a form for it, or which provides generic
support for managing user profiles (I've written this sort of thing,
and found it both extremely easy and extremely handy).

In theory, some other convention for designating the profile model
could be established; requiring it to have a reverse relationship with
a specific name, and then doing introspection on the User model to
find out what's on the other end of that relationship, would be one
possibility. But doing that reliably is a bit tricky, since it
requires knowledge of a couple internal APIs. Getting the appropriate
profile model from the AUTH_PROFILE_MODULE setting, on the other hand,
is a bit easier -- you can get at it through Django's model-loading
API (which, though also technically internal, is far simpler and, at
least in the part you need for this, unlikely to change) or you can
get at it through the contrib.contenttypes framework.

Using AUTH_PROFILE_MODULE and get_profile() also helps to clarify to
other people what your code is doing: by now it's such an established
convention that people who look at your applications will immediately
understand what's going on.


--
"Bureaucrat Conrad, you are technically correct -- the best kind of correct."

Glenn Maynard

unread,
Apr 13, 2009, 8:32:56 PM4/13/09
to django-d...@googlegroups.com
On Mon, Apr 13, 2009 at 8:02 PM, James Bennett <ubern...@gmail.com> wrote:
> user -- they behaved more like normal attributes. At that point
> get_profile() could have been refactored into a read-only property,
> but there really weren't any pressing API-design reasons for doing so.

Sure; that's mostly what I was curious about--whether get_profile() as
an API predated the ORM's current relationship API and was mostly
historical, since it mostly seems like duplicate functionality.

> That means you can write generic code which, for example, looks up the
> profile model and generates a form for it, or which provides generic
> support for managing user profiles (I've written this sort of thing,
> and found it both extremely easy and extremely handy).

Well, you might want to do that for any model, and the admin API
provides a more generic approach to managing this sort of task--but
OK.

It also assumes that there's just one type of profile--I'd think that
an app that wants to store information per user would want to have its
own model, since expecting the "main" user of the site to add some
boilerplate entries to his profile model doesn't seem very nice. The
reverse OneToOne mappings mostly scale to this sort of thing cleanly.

> Using AUTH_PROFILE_MODULE and get_profile() also helps to clarify to
> other people what your code is doing: by now it's such an established
> convention that people who look at your applications will immediately
> understand what's going on.

I don't think "user.get_profile()" is measurably clearer than
"user.profile", though I guess it would be if you named your profile
class "Teapot".

--
Glenn Maynard

James Bennett

unread,
Apr 13, 2009, 8:55:51 PM4/13/09
to django-d...@googlegroups.com
On Mon, Apr 13, 2009 at 7:32 PM, Glenn Maynard <glennf...@gmail.com> wrote:
> Well, you might want to do that for any model, and the admin API
> provides a more generic approach to managing this sort of task--but
> OK.

It's true you *might* want to do it for any particular model, but the
specific case of user profiles is such a common situation that it
seems a shame to require everybody to come up with their own system
and their own helpers and such. That's why Django has a standard API
for handling user profiles.

> It also assumes that there's just one type of profile--I'd think that
> an app that wants to store information per user would want to have its
> own model, since expecting the "main" user of the site to add some
> boilerplate entries to his profile model doesn't seem very nice.  The
> reverse OneToOne mappings mostly scale to this sort of thing cleanly.

It does assume only one profile model, but that's by far the common
case, and so that's what's supported. More complex multi-profile
situations veer off into the realm of things that can't really be
supported by an API that's both simple and generic, and have such
varied use-cases (e.g., how do you decide which type of user gets
which type of profile? Do some users get multiple profiles? Do users
move from one type to another over time?) that asking people to write
their own custom code is more acceptable, since so many of those will
be one-off/highly-specialized systems anyway.

> I don't think "user.get_profile()" is measurably clearer than
> "user.profile", though I guess it would be if you named your profile
> class "Teapot".

Well, the thing is that Django's had this API for years now, and
people are familiar with it; while I'm sure it's possible to come up
with other options which may or may not be "clearer", get_profile()
has the advantage of already being a known, standard idiom in Django,
which means people are more likely to recognize it and know what your
code is doing than if you come up with your own system.

Glenn Maynard

unread,
Apr 13, 2009, 9:34:32 PM4/13/09
to django-d...@googlegroups.com
On Mon, Apr 13, 2009 at 8:55 PM, James Bennett <ubern...@gmail.com> wrote:
> It's true you *might* want to do it for any particular model, but the
> specific case of user profiles is such a common situation that it
> seems a shame to require everybody to come up with their own system
> and their own helpers and such. That's why Django has a standard API
> for handling user profiles.

You don't need to come up with helpers--OneToOneField automatically
creates the only helper this provides, in a way that (unlike
get_profile()) is consistent with all other model relationships. It's
standardizing how to get to something while leaving that something
completely undefined, so you can't actually do much of anything with
it except stuff like auto-generated forms. It just seems like a
narrowly useful special case.

> Well, the thing is that Django's had this API for years now, and
> people are familiar with it; while I'm sure it's possible to come up
> with other options which may or may not be "clearer", get_profile()
> has the advantage of already being a known, standard idiom in Django,
> which means people are more likely to recognize it and know what your
> code is doing than if you come up with your own system.

If someone can't instantly understand that "user.profile" refers to
the user's profile, they're already in trouble. :)

Anyhow, even if it seems like a funny special case to me, it's
probably a total of twenty lines of code in auth, so it's harmless. I
just wanted to know if there was some deeper design concept behind it.

Also, it's not mutually exclusive with accessing the profile
directly--is there any reason the docs shouldn't recommend
OneToOneField instead of ForeignKey for the profile's user field?
It's a better fit; this seems like a relic from before OneToOneField
was stable. http://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users

--
Glenn Maynard

James Bennett

unread,
Apr 14, 2009, 12:00:00 AM4/14/09
to django-d...@googlegroups.com
On Mon, Apr 13, 2009 at 8:34 PM, Glenn Maynard <glennf...@gmail.com> wrote:
> You don't need to come up with helpers--OneToOneField automatically
> creates the only helper this provides, in a way that (unlike
> get_profile()) is consistent with all other model relationships.  It's
> standardizing how to get to something while leaving that something
> completely undefined, so you can't actually do much of anything with
> it except stuff like auto-generated forms.  It just seems like a
> narrowly useful special case.

Well, first of all user profiles aren't a "narrowly useful special
case" -- they're an extremely common feature needed on lots of
real-world sites. So having some sort of standard API for that is a
good thing.

Second, I'm not sure why you're devoting so much energy to what's
basically a bikeshed argument (we painted it "get_profile()", and you
want it to be painted "profile"). Yes, it's named that way for largely
historical reasons, but to change it at this point, given our
compatibility policy, we'd need conclusive proof that it's completely
broken. Which it isn't: it works just fine, you just happen to not
like the name.

Third, as I've pointed out, basing it off AUTH_PROFILE_MODULE makes
things like determining the profile model a bit easier (to get at it
from nothing but a related name, you need to know enough undocumented
stuff to walk your way across a couple of internal-only objects).

It also avoids any confusion that might come from someone sticking an
attribute of the wrong name onto a User ('profile' can and probably
does mean "related object storing user-specific profile information",
but it doesn't necessarily mean that, and having people think you're
implementing an interface when you're not is risky), and makes it a
bit easier to work around potential namespace collisions (what happens
if you're using two apps, each of which has a model that can be used
as the profile? Under a magical-reverse-name scheme one or the other
would blow up with an accessor collision, but with AUTH_PROFILE_MODULE
you don't get forced into naming the relation a particular way and so
can avoid this problem).

> Also, it's not mutually exclusive with accessing the profile
> directly--is there any reason the docs shouldn't recommend
> OneToOneField instead of ForeignKey for the profile's user field?
> It's a better fit; this seems like a relic from before OneToOneField
> was stable.  http://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users

Personally I'm still a bit wary of OneToOneField. Not for any reason
related to the code or technical issues, but simply because of what it
implies -- OneToOneField is most useful for handling the link between
two classes in a multi-table inheritance situation (which is how
Django makes use of it internally), and any time I see a OneToOneField
my instinctive reaction is "oh, this is meant to behave like a
subclass of that other model".

A unique ForeignKey, on the other hand, doesn't carry any such semantics.

Glenn Maynard

unread,
Apr 14, 2009, 12:27:17 AM4/14/09
to django-d...@googlegroups.com
On Tue, Apr 14, 2009 at 12:00 AM, James Bennett <ubern...@gmail.com> wrote:
> Well, first of all user profiles aren't a "narrowly useful special
> case" -- they're an extremely common feature needed on lots of
> real-world sites. So having some sort of standard API for that is a
> good thing.

Obviously, I didn't say user profiles were narrow; I said the benefit
of having this special case is limited.

> Second, I'm not sure why you're devoting so much energy to what's

I'm not. I keep saying it's not a big deal, doesn't really need to be
changed, and that I just wanted to know how it got that way; you keep
writing essays in response. I'm going to elide the rest so I don't
get another essay dropped on my head.

> Personally I'm still a bit wary of OneToOneField. Not for any reason
> related to the code or technical issues, but simply because of what it
> implies -- OneToOneField is most useful for handling the link between
> two classes in a multi-table inheritance situation (which is how
> Django makes use of it internally), and any time I see a OneToOneField
> my instinctive reaction is "oh, this is meant to behave like a
> subclass of that other model".

I find the "zero or one to one" case much more commonly useful, and
OneToOneField is much more natural for that (because ForeignKey's
reverse gives a set); but that's the subject of another thread (and,
of course, that topic is what led me to wonder about this one).

--
Glenn Maynard

David Cramer

unread,
Apr 15, 2009, 4:04:06 AM4/15/09
to Django developers
I was never a fan of the profile model as it stands. It's not very
practical in every situation I've ever been in. Being that 1.0 was
supposed to be backwards compatible, and this is a public API I think
it needs to stay.

I'd love to see a way to swap out the User model in the future though,
rather than attach an additional db call for profiles. However, the
one-to-one approach is a lot nicer than get_profile().

On Apr 13, 11:27 pm, Glenn Maynard <glennfmayn...@gmail.com> wrote:

mrts

unread,
Apr 15, 2009, 6:39:38 PM4/15/09
to Django developers


On Apr 15, 11:04 am, David Cramer <dcra...@gmail.com> wrote:
> I was never a fan of the profile model as it stands. It's not very
> practical in every situation I've ever been in. Being that 1.0 was
> supposed to be backwards compatible, and this is a public API I think
> it needs to stay.
>
> I'd love to see a way to swap out the User model in the future though,
> rather than attach an additional db call for profiles. However, the
> one-to-one approach is a lot nicer than get_profile().

http://code.djangoproject.com/ticket/3011 has a lengthy discussion on
this theme.
Reply all
Reply to author
Forward
0 new messages