23

Let's assume that we have the following models.

class A(Model): pass
class B(Model): pass

Then there is no difference between:

In model A: b = OneToOneField(B, related_name=A.__name__)

and

in model B: a = OneToOneField(A, related_name=B.__name__)

So what questions should I ask myself to decide whether to put OTO in one model or another. I mean like has-a, is-a and so on.

aemdy
  • 3,702
  • 6
  • 34
  • 49

3 Answers3

46

There actually is a difference in where you put the one-to-one field, because deletion behaves differently. When you delete an object, any other objects that had one-to-one relationships referencing that object will be deleted. If instead you delete an object that contains a one-to-one field (i.e. it references other objects, but other objects are not referencing back to it), no other objects are deleted.

For example:

class A(models.Model):
    pass

class B(models.Model):
    a = models.OneToOneField(A)

If you delete A, by default B will be deleted as well (though you can override this by modifying the on_delete argument on the OneToOneField just like with ForeignKey). Deleting B will not delete A (though you can change this behavior by overriding the delete() method on B).

Getting back to your initial question of has-a vs. is-a, if A has a B, B should have the one-to-one field (B should only exist if A exists, but A can exist without B).

djvg
  • 11,722
  • 5
  • 72
  • 103
Erin Heyming
  • 1,521
  • 1
  • 13
  • 12
  • 1
    Note this assumes `on_delete=CASCADE`, which was the default [before Django 2.0](https://docs.djangoproject.com/en/stable/releases/2.0/#features-removed-in-2-0). – djvg Aug 19 '21 at 12:44
10

OneToOneFields are really only for two purposes: 1) inheritance (Django uses these for its implementation of MTI) or 2) extension of a uneditable model (like creating a UserProfile for User).

In those two scenarios, it's obvious which model the OneToOneField goes on. In the case of inheritance, it goes on the child. In the case of extension it goes on the only model you have access to.

With very few exceptions, any other use of a one-to-one should really just be merged into one single model.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • What about table locking? If we were to separate specific data (such as a salary table from an Employee table), we could use `select_for_update` on the Employee table without locking the salary information. Correct? – grokpot Nov 09 '16 at 20:55
  • Another valid use-case for a `OneToOneField` would be when a field is moved into a separate model as an alternative to `QuerySet.defer()`, as suggested in the *Note* in the [defer() documentation](https://docs.djangoproject.com/en/stable/ref/models/querysets/#defer): "... the best choice you can make is to normalize your models and put the non-loaded data into a separate model ..." – djvg Aug 19 '21 at 12:27
0

By the way, I needed OneToOneField to prevent circular dependency (inheritance use):

Model A:
  ...
  current_choice = models.ForeignKey(B)

Model B:
  ...
  parent = models.ForeignKey(A)

That means, A needs B to be defined. Not a good database convention for bootstrapping. Instead I did:

Model A:
  ...

Model B:
  ...
  parent = models.ForeignKey(A)

Model C:
  parent = models.OneToOneField(A)
  current_choice = models.ForeignKey(B)

With respect to example from documentation, you can also have clean queries like: p1.restaurant.place.restaurant.place... This is madness.

hurturk
  • 5,214
  • 24
  • 41
  • 1
    Why you did not realise this behaviour by simply using the *related_name* on the foreign key? So on Model A you could do `current_choice = models.ForeignKey(B, related_name='parent')` and then on every instance of B (`b = B.objects.first()`) you can do `b.parent` to fetch the related A-object. – Kim Jan 18 '17 at 15:34
  • Well, `related_name` is not necessarily one-to-one but used as a set query most of the time, so you need check for count etc. Example above is rather an immediate query as it has been forced to have a single result only. – hurturk Jan 21 '17 at 16:24