【问题标题】:Check queryset is empty in Django检查 Django 中的查询集是否为空
【发布时间】:2014-09-26 11:34:33
【问题描述】:

假设我有一段代码,例如:

class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'

    def get_queryset(self):
        """
        Excludes any questions that aren't published yet.
        """
        all_entries = Choice.objects.all()

        if not all_entries:
            return Question.objects.filter(pub_date__lte=timezone.now())

我试图从一个问题中获取所有选项,如果没有可用的选项,则返回 404。但是我只设法实现了它的一部分并得到了错误:

“NoneType”对象没有“过滤器”属性

这是从它提到的Django tutorial的最底部获取的

例如,可以在没有选择的网站上发布问题是很愚蠢的。因此,我们的观点可以检查这一点,并排除此类问题。

我哪里错了?

编辑:

我将引用“all_entries”的代码更改为:

all_entries = Choice.objects.all().count()

if all_entries > 0:
    return Question.objects.filter(pub_date__lte=timezone.now())

但这只是返回所有问题,无论他们是否有选择......

Models.py

from django.db import models
import datetime
from django.utils import timezone

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

    def __str__(self):              # __unicode__ on Python 2
       return self.question_text

    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
    was_published_recently.admin_order_field = 'pub_date'
    was_published_recently.boolean = True
    was_published_recently.short_description = 'Published recently?'


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

    def __str__(self):              # __unicode__ on Python 2
        return self.choice_text

为 cms_mgr 编辑

基本上我想检查与指定问题相关的选项数是否为空。当我转到此链接时 - http://127.0.0.1:8000/polls/3/ 我想从 id ('3') 中获取问题并检查它包含的选项数量。

【问题讨论】:

  • Question 型号代码是什么样的?

标签: python django


【解决方案1】:

当且仅当他们有相关选择时,获取所有问题的一种方法是获取Choices 的列表而不是问题,然后根据这些问题评估您的查询。例如:

Question.objects.filter(pk__in=[x.question.pk for x in Choice.objects.all()])

方括号内的位是list comprehension。列表推导非常有用,值得了解。在上面的代码中,列表理解将首先评估。基本上说的是'对于Choice.objects.all() 中的每个x,将x.pk 放入此列表中'。然后查询集将返回至少存在一个相关Choice 的每个Question

如果您想要的是每个QuestionChoices 查询集,那么它们已经可供您使用。对于Question 的任何实例,我们将调用我们的q,您可以使用q.choice_set.all() 获取其关联的Choices,如果没有则返回空。

实施:首先更改您的DetailView 的名称以避免与通用名称混淆。我们称之为QuestionDetailView。添加context_object_name 以使您的模板以后更具可读性。不要修改默认查询集以排除未发布的问题,因为正确的方法是使用model manager

class QuestionDetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'
    context_object_name = 'question'

在您用于该视图的模板中,您已经可以使用{% for choice in question.choice_set.all %} 之类的内容来选择问题的相关选项。 question 之所以称为 context_object_name,是因为您可以使用任何您喜欢的名称。请注意,模板中的all 后面不需要()

如果您需要在将选项返回到您的模板之前对它们进行一些其他工作,您可以在视图中执行此操作。因此,在您的 QuestionDetailView 中,您可以添加:

def get_context_data(self, **kwargs):
    # call the base implementation to get original context
    context = super(DetailView, self).get_context_data(**kwargs)
    context['choices'] = self.object.choice_set.all() # your question's choice set, to manipulate
                                                      # as you see fit
    return context

我会解释这里发生了什么。这为视图将返回的内容添加额外的上下文以供您的模板使用。我编写的代码只会返回所有问题的选择,因此不会添加到已经存在的内容中,但您可以执行您关心的任何其他操作。然后,修改后的选择集将在您的模板中以choices 的形式提供,因此您可以使用{% for choice in choices %}

无论您以何种方式选择一个空的选项集都是可能的,并且您可以轻松地满足它(例如模板中的{% if choices %}{% if question.choice_set.count %})。您可能希望比 404 更优雅地处理任何空查询集,因为您真的不希望用户被引导到错误页面以获得可预测的结果,例如空查询集。

【讨论】:

  • 什么是'x'?这看起来和你想的一样吗? Question.objects.filter(pk__in=[Question.id for x in Choice.objects.all()])
  • 谢谢,这至少现在可以编译了,但返回的选项似乎只有 2 个。我有 3 个问题 - 一个包含 3 个选项,一个包含 2 个选项,一个为空。我期待总共有5个选择。另外,有没有办法通过这个列表中的问题 id 获得选择?
  • 有吗?您的代码似乎从所有问题中获得了全部选择。获得 2 的值让我感到困惑,因为一方面,总共有 5 个选择,另一方面,我需要获得该特定问题的选择数量。
  • 哦,当您说“...是获取选项列表而不是问题...”时,您的回答让我感到困惑。我一定已经有了这个问题,因为“Question.objects.filter(pub_date__lte=timezone.now())”返回了它的选择。不知何故,我需要得到那个问题的 id,然后从中得到它的选择。
  • 请为您查看编辑。
【解决方案2】:

如果 all_entries 为空,则您不会返回任何内容。如果函数没有显式返回值,Python 会返回 None

【讨论】:

  • 谢谢,这防止了错误,但现在无论选择多少,它都会显示问题。
【解决方案3】:

您尝试做的事情并不像看起来那么容易。您必须按选项数量过滤问题查询集,而不是检查选项是否存在然后返回问题。

您应该查看this other answer 以了解如何使用注释来完成此操作。

【讨论】:

  • 没有更简单的方法吗?我已经有了 Question 对象,你不能以某种方式从对象中获取选择吗?
  • 此时您可以使用choice_set。对于任何Questionq,其相关的Choicesq.choice_set.all()
【解决方案4】:

也许值得一提的是,本系列后面有一个教程part 7,提示了从根目录处理以下问题的正确方法。

例如,可以在没有选项的网站上发布问题是很愚蠢的。因此,我们的观点可以检查这一点,并排除此类问题。

我认为解决它的最佳方法是首先避免创建“问题没有选择”(假设没有人会直接使用数据库)。 本教程教你如何修改管理站点页面,我们应该只允许管理员添加至少有 2 个选项的问题。

【讨论】:

    猜你喜欢
    • 2020-08-27
    • 2012-04-14
    • 2017-12-26
    • 2010-11-26
    • 2019-07-20
    • 1970-01-01
    • 2013-06-30
    • 2011-04-24
    • 2021-01-05
    相关资源
    最近更新 更多