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

时间:2011-08-03 15:07:16

标签: django django-models

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

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

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

在这种情况下,每个Dude可以有多个PhoneNumber,但这种关系应该是单向的,因为我不需要从PhoneNumber {{1}知道它拥有它本身,因为我可能有很多不同的对象拥有Dude个实例,例如PhoneNumber

Business

我会在模型中用什么替换class Business(models.Model): numbers = models.OneToManyField('PhoneNumber') (不存在)来表示这种关系?我来自Hibernate / JPA,宣布一对多关系就像:

OneToManyField

我如何在Django中表达这一点?

9 个答案:

答案 0 :(得分:104)

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

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

https://docs.djangoproject.com/en/dev/ref/models/fields/#foreignkey

示例中的当前结构允许每个Dude有一个数字,每个数字属于多个Dudes(与Business相同)。

如果你想要反向关系,你需要在PhoneNumber模型中添加两​​个ForeignKey字段,一个到Dude,一个到Business。这将允许每个号码属于一个Dude或一个Business,并且Dudes和Business可以拥有多个Numbers。我想这可能就是你所追求的。

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

答案 1 :(得分:35)

在Django中,一对多关系称为ForeignKey。但是,它仅适用于一个方向,因此您需要

而不是具有类number的{​​{1}}属性。
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的{​​{1}},然后对d对象执行类似操作。

答案 2 :(得分:13)

您可以在OneToMany关系的多方使用外键(即ManyToOne关系)或使用具有唯一约束的ManyToMany(在任何一方)。

答案 3 :(得分:12)

更清楚 - Django中没有OneToMany,只有ManyToOne - 这是上面描述的Foreignkey。您可以使用Foreignkey描述OneToMany关系,但这非常难以理解。

关于它的好文章: https://amir.rachum.com/blog/2013/06/15/a-case-for-a-onetomany-relationship-in-django/

答案 4 :(得分:2)

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)

首先,我们进行游览:

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

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

这里:

关系模型= OneToMany

因此,反向关系模型= ManyToOne

NB: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)

注意:简单地思考!

答案 6 :(得分:0)

如果“许多”模型本身不能证明创建模型的合理性(此处不是这种情况,但它可能会使其他人受益),则另一种选择是通过{{ 3}}

Postgres可以处理Django Contrib packageArray数据类型,并且当 many-ies 只能是绑定到 one 的单个实体。

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

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

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

答案 7 :(得分:0)

虽然rolling stone的回答是好的,简单而实用的,但我认为它不能解决两件事。

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

介绍content types framework,它公开了一些对象,这些对象使我们可以在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)

有关详细信息,请参见docs,或者可以查看this article以获得快速教程。

此外,这里还有an article主张反对使用通用FK。

答案 8 :(得分:0)

其实一对多的关系是很有用的。我做这样的事情:

class Dude(models.Model):
    number = models.ManyToManyField(PhoneNumber)

    def save(self, *args, **kwargs):
        if Dude.objects.get(number=self.number):
            raise Exception("Dude, this number has been used.")
        return super(Dude, self).save(*args, **kwargs)

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

这将确保号码只使用一次。