Search code examples
pythondjangodjango-modelsone-to-one

Usage of OneToOneField in Django


I am very confused about the usage of the OnetoOneField. I thought it was for a case where a given record can only have 1 reference to another table. For example, a Child has 1 Parent.

But it seems that Django makes the field that is defined as OneToOne a primary key of your table, meaning it has a Unique constraint. This doesn't make any sense. Any given Child has only 1 Parent. But there might be more than 1 child with the same parent. The the FK to the parent is not going to be unique in the entire Child table

I wanted to use OneToOne instead of ForeignKey in order to enforce the one-to-one aspect, as opposed to ForeignKey, which is 1 to Many (any given Child can have more than 1 parent).

Am I wrong in my understanding? Should I go back to using ForeignKey and just ensure that my code enforces 1-1?

I found these other links which asked the same question, but not sure that I saw a definitive answer Why would someone set primary_key=True on an One to one reationship (OneToOneField)? OneToOneField() vs ForeignKey() in Django


Solution

  • A OneToOneField might be a bit misleading, in fact it is a zero/one-to-one field. It is typically used to relate instances of model A to instances of model B, but where no two A instances can refer to the same B instance. A typical usecase is for example a if you have a model of Employees, and for some Employees you have to hold extra data. But it is often a bit of an antipattern: if you have control over model B (because it is not implemented in a different package), you can not add (nullable) fields to B, and so then you can make a model A with the extra data, and point a OneToOneField from A to B. This can also be used if the number of fields would be large, and the number of B records that have a related record A is small. In that case, making extra nullable fields could blow up the database immensely. In the Django default packages (the django.contrib) it is not used at all.

    A OneToOneField however has a use-case for which Django uses a OneToOneField in a hidden matter: model inheritance [Django-doc]. Indeed, if you make two models:

    class Foo(models.Model):
        pass
    
    
    class Bar(Foo):
        pass

    Then these are two concrete models. Django implements this by creating two tables, one for foo and one for bar. The primary key of Bar will be a OneToOneField that refers to the Foo table. These thus share the same "id space" and it also ensures that no two Bars are made for a Foo. It however does not prevent making a record Qux with the same id if Qux is also a subclass of Foo, so it is not a "perfect" way to represent inheritance. Furthermore inheritance in relational databases often is not a good idea either, since it will require extra JOINs.

    To summarize: OneToOneFields are often used to add data to an already existing model we don't have much control about, or when the number of records with that extra data is small, and would blow up storage. But it should be used wisely.

    Anecdote: I used to because Django does not make it very clear what session belongs to what user. It is encoded in the session data, but it is not easy to filter for example what sessions belong to a certain user. Then I used a OneToOneField to refer to the Session, to prevent creating a lot of extra records.