Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use one-to-one relationships in Django models

I've read a few places (see the second answer) that one-to-one relationships in Django models should almost always only be used for inheritance, or to access an otherwise inaccessible model (like the Django user model).

However, it seems like there are cases where you have an object that will always have exactly one instance of another object where you would logically want to separate those two objects. Say, for example, your app was storing information about cars. Each car has exactly one driver and each driver only drives one car. Does it not make sense to separate car and driver into two separate models?

like image 605
ekrah Avatar asked Aug 08 '14 15:08

ekrah


2 Answers

Imagine you have a company and make intranet tool listing all employees, their positions, offices, departments, salaries, etc. You would make in Django models.py a class Employee, maybe something like this:

class Employee(models.Model):
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)
    position = models.CharField(max_length=255)
    office = models.CharField(max_length=20)
    salary = models.IntegerField()
    department = models.ForeignKey(Department, related_name='employees')

But for some reasons you don't want the salary to be accessible to all employees, maybe there are many people with redactor status in the admin area, and decide to outsource it to its own model and change the model Employee:

class Employee(models.Model):
    # above attributes
    salary = models.OneToOneField(Salary)

There are certainly other ways to hide this information, but one possibility is to divide the information in two tables even though it is just plain 1:1 relation.

Your company is a software company and you introduce pair programming. Every employee has a pair programming partner. It can be just one programming partner. So you adapt your model again:

class Employee(models.Model):
    # above attributes
    pair_programmer = models.OneToOneField('self')

This would be a recursive one-to-one relation.

One-to-one relations are not very common and hard to find in beginners' tutorials, but if there are some specific requirements, you find yourself creating 1:1 relation for solving the problem.

Here is a real life example from my work. I'm bioinformatician and make software for microorganisms. They are classed in genera and species. Every genus may contain one or more species, but a species can belong to only one genus. That is a clear 1:n relation. But now, a genus has a type species, only one and just one. And the type species can belong to one genus. Here I put models.OneToOneField, besides the models.ForeignKey.

Don't worry a lot about 1:1 relations in advance. When you come to some specific problem, then you'll figure out if you need 1:1 relation.

like image 87
cezar Avatar answered Oct 22 '22 19:10

cezar


The django documentation gives a great answer:

For example, if you were building a database of “places”, you would build pretty standard stuff such as address, phone number, etc. in the database. Then, if you wanted to build a database of restaurants on top of the places, instead of repeating yourself and replicating those fields in the Restaurant model, you could make Restaurant have a OneToOneField to Place (because a restaurant “is a” place; in fact, to handle this you’d typically use inherit Each car has exactly one driver and each driver only drives one car. Does it not make sense to separate car anance, which involves an implicit one-to-one relation).

Django uses OneToOne to model inheritance (and might use it internally, I haven't checked out the source though). I feel like if there is a tool django is providing and you can use that tool in a way that you can defend, why not use it? It seems like it makes sense, that if a car only has one driver, then enforce that in the db using the tools (OneToOneField) that django provides

like image 29
dm03514 Avatar answered Oct 22 '22 19:10

dm03514