我现在正在定义我的 Django 模型,我意识到模型字段类型中没有 OneToManyField
。我确定有办法做到这一点,所以我不确定我错过了什么。我基本上有这样的东西:
class Dude(models.Model):
numbers = models.OneToManyField('PhoneNumber')
class PhoneNumber(models.Model):
number = models.CharField()
在这种情况下,每个 Dude
可以有多个 PhoneNumber
,但关系应该是单向的,因为我不需要从 PhoneNumber
知道哪个 Dude
本身拥有它,我可能会这样做有许多不同的对象拥有 PhoneNumber
实例,例如 Business
:
class Business(models.Model):
numbers = models.OneToManyField('PhoneNumber')
我将在模型中用什么替换 OneToManyField
(不存在)来表示这种关系?我来自 Hibernate/JPA,其中声明一对多关系很简单:
@OneToMany
private List<PhoneNumber> phoneNumbers;
我如何在 Django 中表达这一点?
要在 Django 中处理一对多关系,您需要使用 ForeignKey
。
ForeignKey 的文档非常全面,应该可以回答您的所有问题:
https://docs.djangoproject.com/en/3.2/ref/models/fields/#foreignkey
您示例中的当前结构允许每个 Dude 拥有一个号码,并且每个号码属于多个 Dudes(与 Business 相同)。
如果您想要反向关系,则需要将两个 ForeignKey 字段添加到您的 PhoneNumber 模型,一个给 Dude,一个给 Business。这将允许每个号码属于一个 Dude 或一个 Business,并且 Dudes 和 Businesses 能够拥有多个号码。我想这可能就是你所追求的。
class Business(models.Model):
...
class Dude(models.Model):
...
class PhoneNumber(models.Model):
dude = models.ForeignKey(Dude)
business = models.ForeignKey(Business)
在 Django 中,一对多的关系称为 ForeignKey。但是,它只在一个方向上起作用,因此您需要的不是具有 Dude
类的 number
属性
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)
您可以使用 d.phonenumber_set.objects.all()
访问 Dude
对象 d
的 PhoneNumber
,然后对 Business
对象执行类似操作。
ForeignKey
的意思是“一对一”。使用你上面的例子,我应该有一个有很多 PhoneNumbers
的 Dude
对吗?
ForeignKey(Dude, unique=True)
,则 ForeignKey
仅是一对一的,因此使用上面的代码,您将获得一个带有多个 PhoneNumber
的 Dude
。
PhoneNumber
执行此操作。现在它开始变得有意义了。 ForeignKey
本质上是多对一的,因此您需要向后执行才能获得一对多 :)
phonenumber_set
名称的意义吗?我没有看到它在任何地方定义。它是模型的名称,全部小写,附加“_set”吗?
更清楚一点 - Django 中没有 OneToMany,只有 ManyToOne - 这是上面描述的外键。您可以使用 Foreignkey 来描述 OneToMany 关系,但这非常不具表现力。
关于它的一篇好文章:https://amir.rachum.com/blog/2013/06/15/a-case-for-a-onetomany-relationship-in-django/
您可以在 OneToMany
关系(即 ManyToOne
关系)的许多方面使用外键,也可以使用具有唯一约束的 ManyToMany
(在任何方面)。
Django 足够聪明。实际上我们不需要定义 oneToMany
字段。它将由 Django 自动为您生成。我们只需要在相关表中定义一个foreignKey
。换句话说,我们只需要使用 foreignKey
定义 ManyToOne
关系。
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 的自动生成对象 wheel_set
。对于汽车 c
,您将使用 c.wheel_set.all()
。
ForeignKey(related_name=...)
修改自动生成的名称
虽然 rolling stone 的答案很好、简单且实用,但我认为它没有解决两件事。
如果 OP 想要强制一个电话号码不能同时属于 Dude 和 Business 由于在 PhoneNumber 模型上而不是在 Dude/Business 模型上定义关系,不可避免地会感到悲伤。当外星人来到地球时,我们想要添加外星人模型,我们需要修改电话号码(假设外星人有电话号码),而不是简单地在外星人模型中添加“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。
首先我们参观一下:
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 不提供任何 OneToMany 关系。所以我们不能在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)
注意:简单思考!!
实际上,一对多关系非常有用。我做这样的事情:
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(...)
这将确保该号码仅使用一次。
如果“多”模型不能证明创建模型本身是合理的(这里不是这种情况,但它可能会使其他人受益),另一种选择是依赖特定的 PostgreSQL 数据类型,通过 Django Contrib package
Postgres 可以处理 Array 或 JSON 数据类型,当 many-ies 只能绑定到一个。
Postgres 允许您访问数组的单个元素,这意味着查询可以非常快,并避免应用程序级别的开销。当然,Django implements a cool API 可以利用此功能。
它显然具有不能移植到其他数据库后端的缺点,但我认为它仍然值得一提。
希望它可以帮助一些寻找想法的人。
一对多关系意味着一个模型记录可以具有与其关联的许多其他模型记录。
from django.db import models
class Menu(models.Model):
name = models.CharField(max_length=30)
class Item(models.Model):
menu = models.ForeignKey(Menu)
name = models.CharField(max_length=30)
description = models.CharField(max_length=100)
不定期副业成功案例分享
dude = models.ForeignKey(Dude, related_name='numbers')
,然后您可以使用some_dude_object.numbers.all()
获取所有相关号码(如果您未指定“related_name”,它将默认为“number_set”)。