[Django]-Difference between ForeignKey(User, unique=True) and OneToOneField

70👍

A OneToOneField is very similar to a ForeignKey with unique=True. Unless you are doing multiple table inheritance, in which case you have to use OneToOneField, the only real difference is the api for accessing related objects.

In the Django docs it says:

Conceptually, this is similar to a ForeignKey with unique=True, but the “reverse” side of the relation will directly return a single object.

Let’s show what that means with an example. Consider two models, Person and Address. We’ll assume each person has a unique address.

class Person(models.Model):
    name = models.CharField(max_length=50)
    address = models.ForeignKey('Address', unique=True)

class Address(models.Model):
    street = models.CharField(max_length=50)

If you start with a person, you can access the address easily:

address = person.address

However if you start with an address, you have to go via the person_set manager to get the person.

person = address.person_set.get() # may raise Person.DoesNotExist

Now let’s replace the ForeignKey with a OneToOneField.

class Person(models.Model):
    name = models.CharField(max_length=50)
    address = models.OneToOneField('Address')

class Address(models.Model):
    street = models.CharField(max_length=50)

If you start with a person, you can access the address in the same way:

address = person.address

And now, we can access the person from the address more easily.

person = address.person # may raise Person.DoesNotExist

1👍

When you access a OneToOneField you get the value of the field you queried. In this example a book model’s ‘title’ field is a OneToOneField:

>>> from mysite.books.models import Book
>>> b = Book.objects.get(id=50)
>>> b.title
u'The Django Book'

When you access a ForeignKey you get the related model object, which you can then preform further queries against. In this example the same book model’s ‘publisher’ field is a ForeignKey (correlating to the Publisher class model definition):

>>> b = Book.objects.get(id=50)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
u'http://www.apress.com/'

With ForeignKey fields queries work the other way too, but they’re slightly different due to the non-symmetrical nature of the relationship.

>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive Into Python>, ...]

Behind the scenes, book_set is just a QuerySet and can be filtered and sliced like any other QuerySet. The attribute name book_set is generated by appending the lower case model name to _set.
I hope this helps illustrate the differences between the relationships created.

👤Yup.

Leave a comment