在两个相关模型中,哪一个应该包含关系的定义?
On two related models, which one should contain the definition of the relationship?
首先,是的:我读过 Django 的 foreign key and many-to-many documentation,但我仍然不是 100% 清楚如何在实际层面上实现关系,尤其是关于层次结构的关系。
- 一对一
我知道如何建立一对一的关系。然而,在更概念化的层面上,哪个模型应该包含对另一个模型的引用?假设我有一个 Citizen
和一个 Passport
。现在,很明显,一个 Citizen
只能有一个 Passport
,反之亦然,但是 理想情况下 ,Citizen
是否应该包含一个字段引用他的 Passport
,或者 Passport
模型是否应该包含对它所属的 Citizen
的引用?
- 多对多
为了简单起见,假设我有一个 Person
模型和一个 Trip
模型(Trip
就像出去旅行一样)。许多 Persons
可以参加单个 Trip
。或者换句话说:一个Person
可以参与很多Trips
,而在任何一个Trip
中,很多Persons
可以参与。这看起来像多对多关系,但是,理想情况下 ,哪个模型应该包含关系的定义,Person
和 trips
字段还是带有 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 模型中,因为它没有告诉我们关于父项的任何信息,但它确实告诉我们子项属于哪个父项。
首先,是的:我读过 Django 的 foreign key and many-to-many documentation,但我仍然不是 100% 清楚如何在实际层面上实现关系,尤其是关于层次结构的关系。
- 一对一
我知道如何建立一对一的关系。然而,在更概念化的层面上,哪个模型应该包含对另一个模型的引用?假设我有一个 Citizen
和一个 Passport
。现在,很明显,一个 Citizen
只能有一个 Passport
,反之亦然,但是 理想情况下 ,Citizen
是否应该包含一个字段引用他的 Passport
,或者 Passport
模型是否应该包含对它所属的 Citizen
的引用?
- 多对多
为了简单起见,假设我有一个 Person
模型和一个 Trip
模型(Trip
就像出去旅行一样)。许多 Persons
可以参加单个 Trip
。或者换句话说:一个Person
可以参与很多Trips
,而在任何一个Trip
中,很多Persons
可以参与。这看起来像多对多关系,但是,理想情况下 ,哪个模型应该包含关系的定义,Person
和 trips
字段还是带有 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 模型中,因为它没有告诉我们关于父项的任何信息,但它确实告诉我们子项属于哪个父项。