Skip to content

OneToOne Fields And The Primary Key Issue

by Paul Kenjora on June 23rd, 2008

Django provides one-many ForeignKey, many-many ManyToManyField, and one-one OneToOneField options for defining table relationships. The OneToOneField specifically has recently been the topic of heated debates and ideas. Unlike the other options this field is by always constrained to be a “Primary Key”. The restriction implies that each table may ONLY have one OneToneField. Why is this wrong?

OneToOneField Primary Key Constraint Is Wrong

The “Primary Key” constraint (you cannot turn it off) is wrong because a schema often needs to define a property that can be applied to multiple objects. This property is the same but can be one-one with different tables, its a mutable property. Here is a great example:

  1. Tip (table describing tipping rules) applies to a Person (1-1).
  2. Tip (same rules) applies to Restaurant (1-1).
  3. I want to describe the tipping rules for a Person or a specific Restaurant (or a combination of the two). Each case is a (1-1). A foreign key would complicate things here and not reflect the true meaning of Tip. Tip is always 1-1 with at least one other table, that table can vary though.

This is a very common approach for adding properties to objects. I understand the desire to have one-one raltionships describe only is-a relations but they can and should be so much more powerful. I’ve documented the problem on Django’s Ticket #7367: Cannot have non-primary-key OneToOneField to parent class. The debate over the pure is-a constraint is strange because the workaround to the “Primary Key” issue is a quick 3 liner that does not violate the is-a relationship.

Fixing The OneToOneField Primary Key Constraint

The solution is to use “Primary Key” and thus preserve is-a behavior where appropriate but add the ability to make the relationship optional. I decided to use “null=True” as an indicator that the relationship is optional, same convention as ForeignKey. Once the field becomes optional then its constraint changes to “Unique”. The code below achieves this:

Around Line 585 in Django File: db/models/fields/related.py

So its possible to have a clean is-a and more mutable is-a relationship without breaking the system. I’m surprised that Django core developers are constraining the database layer, it seems to violate the coolness of Django + Python. I’ve always liked that the Django framework gives me so much out of the box and at the same time allows me to super customize my projects. For no other reason but to keep Django awesome lets not add artificial constraints where they do not need to exist.

blog comments powered by Disqus