在Django模型中何时使用一对一关系?
我看到一些地方提到,在Django模型中,一对一的关系通常应该只用于继承,或者用来访问一些平时无法直接访问的模型,比如Django的用户模型。
不过,有时候你会遇到一种情况,就是一个对象总是会有一个对应的对象,而你可能会想把这两个对象分开来处理。举个例子,假设你的应用是用来存储汽车的信息。每辆车都有一个司机,而每个司机只开一辆车。这样看来,把车和司机分成两个不同的模型是不是更合理呢?
3 个回答
OneToOneField() 是用来扩展 用户模型 的,这样可以添加一些额外的字段,下面会有示例。*你可以查看 我的回答,里面解释了如何使用 OneToOneField()
来添加额外的字段:
# "account/models.py"
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(
User,
verbose_name=_("user"),
on_delete=models.CASCADE
)
age = models.PositiveSmallIntegerField(_("age"))
gender = models.CharField(_("gender"), max_length=20)
married = models.BooleanField(_("married"))
Django的文档给出了一个很好的解释:
比如说,如果你在建立一个“地点”的数据库,你会在数据库里建立一些常规的信息,比如地址、电话号码等等。然后,如果你想在这些地点的基础上建立一个餐厅的数据库,干嘛要重复之前的字段呢?你可以让餐厅模型和地点模型之间建立一个一对一的关系(因为餐厅“就是”一个地点;实际上,处理这个关系时,通常会用到继承的概念。每辆车都有一个司机,而每个司机只开一辆车。把车和司机分开管理,这样的设计就不太合理了)。
Django使用一对一关系来建模继承(可能在内部也会用到这个关系,不过我还没去查看源代码)。我觉得如果Django提供了一个工具,而你能用这个工具来合理地解决问题,那为什么不使用呢?这看起来很有道理:如果一辆车只有一个司机,那就用Django提供的工具(OneToOneField)在数据库中强制执行这个规则。
想象一下,你有一家公司的内部工具,用来列出所有员工的信息,比如他们的职位、办公室、部门、薪水等等。你可能会在Django的models.py文件里创建一个叫做Employee的类,可能长得像这样:
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')
但是出于某些原因,你不想让所有员工都能看到薪水。也许在管理区域有很多人有编辑权限,他们可能会决定把薪水信息单独放到一个模型里,然后修改Employee模型:
class Employee(models.Model):
# above attributes
salary = models.OneToOneField(Salary)
当然,还有其他方法可以隐藏这些信息,但一种可能的做法是把信息分成两个表,尽管它们之间的关系其实是1:1。
假设你的公司是一家软件公司,并且你引入了结对编程。每个员工都有一个编程搭档,可能只有一个搭档。所以你又要调整你的模型:
class Employee(models.Model):
# above attributes
pair_programmer = models.OneToOneField('self')
这就形成了一个递归的1:1关系。
1:1关系并不常见,在初学者的教程中也很少见到,但如果有一些特定的需求,你可能会发现自己需要创建1:1关系来解决问题。
这里有一个我工作中的真实例子。我是一名生物信息学家,开发微生物的软件。微生物被分类为属和种。每个属可以包含一个或多个种,但一个种只能属于一个属。这是一个明显的1:n关系。但现在,每个属都有一个类型种,只有一个,并且这个类型种只能属于一个属。在这里,我使用了models.OneToOneField
,除了models.ForeignKey
。
不要过于担心1:1关系。等你遇到具体问题时,你自然会明白是否需要使用1:1关系。