Django: When extending User, better to use OneToOneField(User) or ForeignKey(User, unique=True)?
Asked Answered
H

3

31

I'm finding conflicting information on whether to use OneToOneField(User) or ForeignKey(User, unique=True) when creating a UserProfile model by extending the Django User model.

Is it better to use this?:

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)

or this?:

class UserProfile(models.Model):
    user = models.OneToOneField(User)

The Django Doc specifies OneToOneField, while the Django Book example uses ForeignKey.

James Bennett also has two Blog posts that providing conflicting examples as well:

In the former post, Bennett provides some reasons why he switched to using ForeignKey instead of OneToOneField, but I don't quite get it, especially when I see other posts that recommend the opposite.

I'm curious to know your preference and why. Or, does it even matter?

Henton answered 11/7, 2010 at 3:25 Comment(3)
I see nothing in Bennett's first article regarding user profiles.Tittletattle
@Ignacio - Sorry, I added a link to the wrong article. It should have been to his post on "Extending the User Model". I've corrected the link. Thanks for pointing this out.Henton
For me, #5871037 is more clear to understandGuideboard
T
21

The only real reason given in the article is that it can be set up so that the admin page for User will show both the fields in User and UserProfile. This can be replicated with a OneToOneField with a little elbow grease, so unless you're addicted to showing it in the admin page with no work at the cost of a bit of clarity ("We can create multiple profiles per user?! Oh no, wait, it's set unique.") I'd use OneToOneField.

Tittletattle answered 11/7, 2010 at 5:8 Comment(0)
X
7

Besides the admin page inlines, other reason for the ForeignKey solution is that it allows you to use the correct, default DB manager when objects are accessed with a reverse relation. Consider example from this subclasses manager snippet. Let's say that the Post class definition from the example looks like this:

class Post(ParentModel):
    title = models.CharField(max_length=50)
    onetoone = models.ForeignKey(SomeModel, unique=True)

    children = ChildManager()
    objects = models.Manager()

By calling somemodel_instance.post_set.all()[0], you get the desired subclasses objects of the Post class as indicated by defining the first (default) manager as a ChildManager. On the other hand, with OneToOneField, by calling somemodel_instance.post you get the Post class instance. You can always call somemodel_instance.post.subclass_object and get the same result, but the default manager could do any other sort of tricks and the FK solutions hides them nicely.

If you own and can modify the custom manager code you can use the use_for_related_fields attribute instead of using FK in place of legitimate 1to1 field, but even that can fail because of some not-known to me nuisances of the automatic managers. As far as I remember it will fail in the above example.

Xochitlxp answered 9/8, 2010 at 17:2 Comment(0)
X
5

Other reason to generally not use the OneToOneField related to reverse relations: when you use reverse relations defined via OneToOneField you get an model instance, contrary to Manager for ForeignKey reverse relation and as a consequence there's always a DB hit. This is costly if you do some generic stuff on reverse relations (via _meta.get_all_related_objects()) and do not know and care if you will use them all or not.

Xochitlxp answered 17/11, 2010 at 15:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.