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

时间:2018-02-20 10:41:42

标签: django database-design django-models model relational-database

首先,是的:我已经阅读过Django的foreign key and many-to-many documentation,但我仍然没有100%明确如何在实际层面上实现关系,特别是关于关系的层次结构。

  1. 一对一的
  2. 我知道如何形成一对一的关系。但是,在更概念的层面上,哪个模型应该包含对另一个模型的引用?我们假设我有CitizenPassport。现在,很明显一个Citizen只能有一个Passport而反之,理想,如果Citizen包含一个字段引用到Passport,或Passport模型是否包含对其所属的Citizen的引用?

    1. 许多对多
    2. 为了简单起见,我们假设我有一个Person模型和一个Trip模型(Trip,就像出去旅行一样)。许多Persons可以参与一个Trip。或者换句话说:Person可以参与许多Trips,而在任何单Trip中,很多Persons都可以参与。这看起来像是多对多关系,但同样,理想,哪个模型应该包含关系的定义,Person带有trips字段或者带participants字段的旅行?为什么?它是否会产生任何实际差异?

      谢谢。

3 个答案:

答案 0 :(得分:3)

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

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

假设您有这些模型:

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')

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

如果您使用管理员应用程序,这将引导您进行更符合人体工程学的设计。

编辑:使用多对多示例展开

m2m关系的更好示例是StackOverflow - 有问题和标签。一个问题有很多标签,标签有很多问题。让我们说模型看起来像这样:

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()

为什么我们将关系置于问题中?这应该是非常合理的 - 在创建新问题时,您需要为其设置标记。创建新标签时,您不关心与之相关的任何问题。您可以创建标记,稍后在创建问题时,将它们与标记相关联 如果标签不存在,您可以在添加新问题时从管理员添加标签。

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

答案 1 :(得分:1)

你应该拥有的心理模型是父母和孩子。每个关系都有两个模型。因此,将其中一个视为父模型或主模型,并将另一个视为子模型或辅助模型。

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

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

对于第一个,我将有一个心理模型,公民是父母,护照是孩子。

Route::get('clubs', function () {

    return App\Clubs::select('name', 'city')->get(); 

});

对于第二个问题,也要这样做。我会选择Person作为父模型,Trip作为子模型。

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)

如果您有class Person(models.Model): name = models.CharField(max_length=255) info = models.TextField() class Trip(models.Model): person = models.ManyToManyField(Person) info = models.TextField() ,则可以使用它来打开数据库,并根据您的模型检查创建的表格。然后,您将更清楚地了解Django如何看待您的模型。

答案 2 :(得分:1)

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

第三种形式告诉我们:

  

“[每个]非密钥[属性]必须提供关于密钥,整个密钥以及密钥的事实。”

因此,对于ForeignKey字段,它应该在Child模型上,因为它不告诉我们关于父项的任何内容,但它确实告诉我们孩子所属的父项。