如何在Django中表达一对多关系


167

我现在正在定义Django模型,我意识到OneToManyField模型字段类型中没有。我确定有办法做到这一点,所以我不确定我缺少什么。我基本上有这样的事情:

class Dude(models.Model):
    numbers = models.OneToManyField('PhoneNumber')

class PhoneNumber(models.Model):
    number = models.CharField()

在这种情况下,每个Dude可以有多个PhoneNumberS,但这种关系应该是单向的,因为我不需要从知道PhoneNumberDude拥有它本身,因为我可能有许多不同的对象自身的PhoneNumber情况下,如Business为例:

class Business(models.Model):
    numbers = models.OneToManyField('PhoneNumber')

我将OneToManyField用模型中的什么替换(不存在)以表示这种关系?我来自Hibernate / JPA,在这里一对多关系的声明很简单:

@OneToMany
private List<PhoneNumber> phoneNumbers;

如何在Django中表达?

Answers:


133

要在Django中处理一对多关系,您需要使用ForeignKey

关于ForeignKey的文档非常全面,应该回答您遇到的所有问题:

https://docs.djangoproject.com/zh-CN/dev/ref/models/fields/#foreignkey

您示例中的当前结构允许每个Dude拥有一个号码,并且每个号码都属于多个Dudes(与Business相同)。

如果需要反向关系,则需要在PhoneNumber模型中添加两​​个ForeignKey字段,其中一个添加到Dude,另一个添加到Business。这样一来,每个号码都可以属于一个Dude或一个公司,并且Dudes和Businesses可以拥有多个号码。我认为这可能是您追求的目标。

class Business(models.Model):
    ...
class Dude(models.Model):
    ...
class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)
    business = models.ForeignKey(Business)

3
鉴于上述问题,您能举个例子吗?我可能只是完全想念它,但是我已经阅读了一段时间的Django文档,对于如何建立这种关系还不清楚。
Naftuli Kay 2011年

4
可能想使两个ForeignKey都不是必需的(blank = True,null = True),或者添加某种自定义验证来确保至少有一个或另一个。拥有通用编号的公司该怎么办?或失业的家伙?
j_syk 2011年

1
@j_syk关于自定义验证的好处。但同时包含要花花公子的外键和业务的外键,然后进行自定义(模型定义外部)验证似乎有点不明智。似乎必须有一种更清洁的方法,但我也无法弄清楚。
andy 2013年

在这种情况下,使用ContentTypes框架是合适的,该框架允许与不同对象建立通用关系。但是,使用内容类型会很快变得非常复杂,如果仅是您的需要,则可能需要这种更简单的方法(如果很棘手)。
kball

57
要提及的一件事是ForeignKey的“ related_name”参数。因此,在PhoneNumber类中dude = models.ForeignKey(Dude, related_name='numbers'),您可以使用它,然后使用some_dude_object.numbers.all()来获取所有相关的号码(如果您未指定“ related_name”,则默认为“ number_set”)。
markhep

40

在Django中,一对多关系称为ForeignKey。但是,它仅在一个方向上起作用,因此您将不需要number拥有类的属性Dude

class Dude(models.Model):
    ...

class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)

许多型号可以有ForeignKey一个其他的模式,所以这将是有效的具有第二属性PhoneNumber,使得

class Business(models.Model):
    ...
class Dude(models.Model):
    ...
class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)
    business = models.ForeignKey(Business)

您可以访问PhoneNumber■对于Dude对象dd.phonenumber_set.objects.all(),然后做一个类似的Business对象。


1
我当时的假设ForeignKey是“一对一”。使用您上面的示例,我应该有Dude很多PhoneNumbers权利?
Naftuli Kay 2011年

6
我修改了答案以反映这一点。是。ForeignKey如果指定ForeignKey(Dude, unique=True),则只能是一对一的关系,因此使用上面的代码,您将获得Dude带有多个PhoneNumbers的a。
stillLearn 2011年

1
@rolling stone-谢谢,我在意识到您评论的错误后补充。Unique = True不能完全像OneToOneField一样工作,我的意思是解释一下,如果您指定Unique = True,则ForeignKey仅使用一对一关系。
stillLearn 2011年

8
+1即可从中执行此操作PhoneNumber。现在,它开始变得有意义了。ForeignKey本质上是多对一的,所以您需要向后做以得到一对多:)
Naftuli Kay 2011年

2
有人可以解释该字段名称的重要性phonenumber_set吗?我没有在任何地方定义它。它的模型名称是否全部小写,并附加“ _set”?
詹姆斯·维尔茨巴


15

您可以在OneToMany关系的许多方面(即ManyToOne关系)使用外键,也可以ManyToMany在唯一的约束下使用(在任何方面)。


8

django很聪明 实际上,我们不需要定义oneToMany字段。它会django为您自动生成:-)。我们只需要foreignKey在相关表中定义。换句话说,我们只需要使用定义ManyToOne关系foreignKey

class Car(models.Model):
    // wheels = models.oneToMany() to get wheels of this car [**it is not required to define**].


class Wheel(models.Model):
    car = models.ForeignKey(Car, on_delete=models.CASCADE)  

如果我们想获取特定汽车的车轮清单。我们将使用python's自动生成的对象wheel_set。对于汽车,c您将使用c.wheel_set.all()


5

尽管滚石乐队的回答是好的,直接的和实用的,但我认为它不能解决两件事。

  1. 如果OP想要强制执行电话号码,则该电话号码不能同时属于Dude和公司
  2. 由于在PhoneNumber模型上而非在Dude / Business模型上定义了关系,因此不可避免地会感到悲伤。当额外的地面到达地球时,我们想添加一个外星人模型,我们需要修改PhoneNumber(假设ET具有电话号码),而不是简单地向外星人模型添加“ phone_numbers”字段。

介绍内容类型框架,它公开了一些对象,这些对象使我们可以在PhoneNumber模型上创建“通用外键”。然后,我们可以在Dude和Business上定义反向关系

from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models

class PhoneNumber(models.Model):
    number = models.CharField()

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    owner = GenericForeignKey()

class Dude(models.Model):
    numbers = GenericRelation(PhoneNumber)

class Business(models.Model):
    numbers = GenericRelation(PhoneNumber)

有关详细信息,请参阅文档,或者查看这篇文章以获取快速教程。

此外,这里是一个文章是论证使用通用FKS的。


0

如果“许多”模型不能证明本身就可以创建模型(在这种情况下不是这样,但是可能会使其他人受益),则另一种选择是通过Django Contrib包依赖特定的PostgreSQL数据类型。

Postgres的可以处理数组JSON数据类型,这可能是一个很好的解决方法来处理一个一对多当许多-IES只能绑在一个单一的实体一个

Postgres允许您访问数组的单个元素,这意味着查询可以非常快速,并避免了应用程序级的开销。当然,Django实现了一个很酷的API来利用此功能。

显然,它具有无法移植到其他数据库后端的缺点,但是我仍然值得一提。

希望它可以帮助一些寻找想法的人。


0

首先,我们进行游览:

01)一对多关系:

ASSUME:

class Business(models.Model):
    name = models.CharField(max_length=200)
    .........
    .........
    phone_number = models.OneToMany(PhoneNumber) (NB: Django do not support OneToMany relationship)

class Dude(models.Model):
    name = models.CharField(max_length=200)
    .........
    .........
    phone_number = models.OneToMany(PhoneNumber) (NB: Django do not support OneToMany relationship)

class PhoneNumber(models.Model):
    number = models.CharField(max_length=20)
    ........
    ........

注意:Django没有提供任何一对多关系。因此,我们不能在Django中使用upper方法。但是我们需要在关系模型中进行转换。所以,我们能做些什么?在这种情况下,我们需要将关系模型转换为反向关系模型。

这里:

关系模型= OneToMany

因此,反向关系模型= ManyToOne

注意:Django支持ManyToOne关系,在Django中,ManyToOne由ForeignKey表示。

02)多对一关系:

SOLVE:

class Business(models.Model):
    .........
    .........

class Dude(models.Model):
    .........
    .........

class PhoneNumber(models.Model):
    ........
    ........
    business = models.ForeignKey(Business)
    dude = models.ForeignKey(Dude)

注意:简单地思考!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.