在两个相关模型中,哪一个应该包含关系的定义?

On two related models, which one should contain the definition of the relationship?

首先,是的:我读过 Django 的 foreign key and many-to-many documentation,但我仍然不是 100% 清楚如何在实际层面上实现关系,尤其是关于层次结构的关系。

  1. 一对一

我知道如何建立一对一的关系。然而,在更概念化的层面上,哪个模型应该包含对另一个模型的引用?假设我有一个 Citizen 和一个 Passport。现在,很明显,一个 Citizen 只能有一个 Passport,反之亦然,但是 理想情况下 Citizen 是否应该包含一个字段引用他的 Passport,或者 Passport 模型是否应该包含对它所属的 Citizen 的引用?

  1. 多对多

为了简单起见,假设我有一个 Person 模型和一个 Trip 模型(Trip 就像出去旅行一样)。许多 Persons 可以参加单个 Trip。或者换句话说:一个Person可以参与很多Trips,而在任何一个Trip中,很多Persons可以参与。这看起来像多对多关系,但是,理想情况下 ,哪个模型应该包含关系的定义,Persontrips字段还是带有 participants 字段的 Trip?为什么?它甚至有任何实际区别吗?

谢谢。

你应该拥有的心智模型是Parent和Child。每一种关系都有两种模式。因此,将一个视为 Parent 模型或主要模型,将另一个视为 Child 模型或辅助模型。

注意:始终将您的关系字段放在 CHILD 模型中。

以下是我将如何解决您的问题:

对于第一个,我会有一个心智模型,公民是 Parent,护照是 child。

class Citizen(models.Model):
    name = models.CharField(max_length=255)
    info = models.TextField()

class Passport(models.Model):
    owner = models.OneToOneField(Citizen)
    unique_no = models.CharField(max_length=30, unique=True)

第二题同理。我会选择 Person 作为 parent 模型,Trip 作为 child 模型。

class Person(models.Model):
    name = models.CharField(max_length=255)
    info = models.TextField()

class Trip(models.Model):
    person = models.ManyToManyField(Person)
    info = models.TextField()

如果您有 sqlitebrowser,您可以使用它来打开您的数据库并检查根据您的模型创建了哪些表。然后,您将更清楚地了解 Django 如何看待您的模型。

这取决于您的业务逻辑。根据经验,我建议考虑管理应用程序。您想如何添加新对象?

添加新对象时,您希望如何添加相关对象?

假设您有这些模型:

Citizen(models.Model):
    name = models.CharField()

Passport(models.Model):
    number = models.CharField()
    citizen = models.OneToOneField('Citizen', related_name='passport')

添加新护照对象时,您可以添加新公民(如果尚不存在)。因为这对我来说看起来不太合逻辑,所以我将关系更改为:

Citizen(models.Model):
    # other fields
    passport = models.OneToOneField('Passport', related_name='citizen')

现在我们可以在管理中添加一个新的公民对象,并在同一页面中添加相关的护照对象。

如果您使用管理应用程序,这应该会让您获得更符合人体工程学的设计。

编辑:使用 many-to-many 示例进行扩展

m2m 关系的更好示例是 Whosebug - 有问题和标签。一个问题有很多标签,一个标签有很多问题。假设模型看起来像这样:

Question(models.Model):
    title = models.CharField()
    body = models.TextField()
    author = models.CharField()
    tags = models.ManyToManyField('Tag', related_name='questions')

Tag(models.Model):
    name = models.CharField()

为什么我们把关系放在问题中?这应该是非常合乎逻辑的——在创建一个新问题时,您希望为其设置标签。创建新标签时,您不关心与其相关的任何问题。您可以创建一个标签,稍后在创建问题时,将它们与标签相关联。
如果标签不存在,您可以在添加新问题时从管理员添加它。

我希望第二个例子更明显。

这背后的理论称为 database normalization,这是一个最佳实践阶梯,如果您想了解更多关于如何构建数据的信息,您应该查阅它。

第三种形式告诉我们:

"[Every] non-key [attribute] must provide a fact about the key, the whole key, and nothing but the key."

所以对于 ForeignKey 字段,它应该在 Child 模型中,因为它没有告诉我们关于父项的任何信息,但它确实告诉我们子项属于哪个父项。