这个Django应用教程中的choice_set是什么?

196 投票
2 回答
35612 浏览
提问于 2025-04-15 17:55

在Django的教程中,有这么一句话,来自于 写你的第一个Django应用,第一部分

p.choice_set.create(choice='Not much', votes=0)

那么,choice_set是怎么产生的,它是什么呢?

我猜choice部分是教程中使用的模型Choice的小写版本,但choice_set又是什么呢?能详细解释一下吗?

更新:根据Ben的回答,我找到了这份文档:反向跟踪关系

2 个回答

3

这里提出了两个重要的问题。

第一个choice_set是怎么产生的?

第二个:它是什么?

我会分享我第一次学习时是如何让自己理解得更简单的。先回答第二个问题,“它是什么”,我用这三个词来解释:模型实例、与该实例相关的对象集合、相关管理器

这是Django教程中的Models.py:

from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

实例

q = Question.objects.get(pk=3)
# Here q is an instance of model class 'Question'.

c = Choice.objects.get(pk=32)
# Here c is an instance of model class 'Choice'.

模型实例就是你数据库中整个的一行数据。

在这里,Question Model作为Choice Model外键使用。因此,所有与实例q相关的对象集合可以通过以下方式进行筛选:

q.choice_set.all()

所以,choice_set指的就是与主键为3的问题相关的所有选择。

现在,回答第一个问题需要用到第三个词相关管理器。Django文档中提到的内容在这里

如果一个模型有外键,那么外键模型的实例将可以访问一个管理器,这个管理器会返回第一个模型的所有实例。默认情况下,这个管理器的名称是FOO_set,其中FOO是源模型的名称,全部小写。这个管理器返回的查询集可以像上面“检索对象”部分所描述的那样进行筛选和操作。

这个词(choice_set)可以通过外键中的'related_name'参数进行更改。

question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name="choices")

对于通过外键的反向关系:

q.choice_set.all()
# If using related_name, then it is the same as 

q.choices.all()
# All the choices related to the instance q.

对于正向关系:

choice_qs = Choice.objects.all()
choice_qs.filter(question=q)
# Same result as above. All the choices related to instance q.
235

你在 Choice 上创建了一个外键,这个外键把每个选择和一个 Question 关联起来。

所以,每个 Choice 都有一个明确的 question 字段,这个字段是在模型中定义的。

Django 的对象关系映射(ORM)也会从 Question 反向跟踪这个关系,自动在每个实例上生成一个叫 foo_set 的字段,其中 Foo 是有外键指向这个模型的模型。

choice_set 是一个 RelatedManager,它可以创建与 Question 实例相关的 Choice 对象的查询集,比如你可以用 q.choice_set.all() 来获取所有相关的选择。

如果你不喜欢 Django 自动选择的 foo_set 这个名字,或者如果你有多个外键指向同一个模型,需要区分它们,你可以使用 related_name 参数在 ForeignKey 中自定义一个名字。

撰写回答