【问题标题】:Bug in Django queryset with count?带有计数的 Django 查询集中的错误?
【发布时间】:2013-07-23 13:18:19
【问题描述】:

您可以猜到我遇到了一个非常奇怪的问题。 我有一个查询集,它什么都不返回(即使它应该返回)但在使用 count() 时仍然返回一个值。

以下是模型:

import hashlib
import random
from django.contrib.auth import get_user_model
from django.db import models


class EmailChangeLogManager(models.Manager):
    def get_query_set(self):
        return super(EmailChangeLogManager, self).get_query_set().filter(state=EmailChangeLog.PENDING)

    def create_new_request(self, user):
        request = self.model(user=user)
        request.save()
        return request


class EmailChangeLog(models.Model):
    """
    logs the users requests to change their email
    """
    PENDING = 0
    CHANGED = 1

    objects = EmailChangeLogManager()

    user = models.ForeignKey(get_user_model())
    token = models.CharField(max_length=40, primary_key=True)  # primary key so it blows up in case of collision
    state = models.SmallIntegerField()
    new_email = models.CharField(max_length=30)

    def __init__(self, user, * args, **kwargs):
        super(EmailChangeLog, self).__init__(self, *args, **kwargs)
        salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
        username = user.username
        if isinstance(username, unicode):
            username = username.encode('utf-8')
        self.token = hashlib.sha1(salt+username).hexdigest()
        self.user = user
        self.state = EmailChangeLog.PENDING

下面是因 IndexError 而失败的视图部分:

if EmailChangeLog.objects.filter(user=self.request.user, state=EmailChangeLog.PENDING).count() > 0:
    context['new_email'] = EmailChangeLog.objects.filter(user=self.request.user, state=EmailChangeLog.PENDING)[0].new_email

我在放置断点后运行了以下几行:

>>> EmailChangeLog.objects.filter(user=self.request.user, state=EmailChangeLog.PENDING).count()
Out[1]: 1
>>> EmailChangeLog.objects.filter(user=self.request.user, state=EmailChangeLog.PENDING)
Out[2]: []
>>> EmailChangeLog.objects.all().count()
Out[3]: 2
>>> EmailChangeLog.objects.filter(user=self.request.user, state=EmailChangeLog.PENDING).count()
Out[4]: 1
>>> EmailChangeLog.objects.filter(user=self.request.user, state=EmailChangeLog.PENDING)
Out[5]: []
>>> EmailChangeLog.objects.all().count()
Out[6]: 2
>>> EmailChangeLog.objects.all()
Out[7]: []
>>> EmailChangeLog.objects.filter(user=self.request.user)
Out[8]: []
>>> EmailChangeLog.objects.filter(state=EmailChangeLog.PENDING)
Out[9]: []
>>> EmailChangeLog.objects.filter()
Out[10]: []
>>> EmailChangeLog.objects.all()
Out[11]: []
>>> EmailChangeLog.objects.all().count()
Out[1]: 2
>>> EmailChangeLog.objects.all()
Out[3]: []
>>> EmailChangeLog.objects.all()
Out[5]: []
>>> EmailChangeLog.objects.all().count()
Out[6]: 2
>>> EmailChangeLog.objects.all().count()
Out[7]: 2
>>> az = EmailChangeLog.objects.all()
>>> az
Out[9]: []
>>> az.count()
Out[10]: 2

这是 django 中的错误吗?如果不是怎么回事?

PS:我使用的是 django 1.5.1

编辑:使用 manage shell_plus 也会发生同样的情况

【问题讨论】:

  • 这不是错误。查询集对象的评估是惰性的——这意味着它不会获取查询集结果,除非它被使用。例如:即使 EmailChangeLog.objects.filter(user=self.request.user, state=EmailChangeLog.PENDING) 返回 [] - 如果你这样做 EmailChangeLog.objects.filter(user=self.request.user, state=EmailChangeLog.PENDING)[:1] - 它会返回一个查询集,因为它在那个时间点被评估。
  • @karthikr:使用索引 0 访问它会引发 IndexError 异常,打印查询也应该消耗它吗?顺便说一句:[:0] 也一样
  • 我的意思是[:1][0]
  • 它因 IndexError 失败:列表索引超出范围
  • @maazza:当你做objects.all()[0]之类的事情时,你会得到什么

标签: python django django-queryset django-1.5


【解决方案1】:

好的,所以我找出了问题出在哪里,它在 init 函数中,我摆脱了它并改用了我的经理的代码,但我仍然认为查询集有问题在 django 这边。

class EmailChangeLogManager(models.Manager):
    def get_query_set(self):
        return super(EmailChangeLogManager, self).get_query_set().filter(state=EmailChangeLog.PENDING)

    def create_new_request(self, user):
        request = self.model()
        salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
        username = user.username
        if isinstance(username, unicode):
            username = username.encode('utf-8')
        request.token = hashlib.sha1(salt+username).hexdigest()
        request.user = user
        request.save()
        return request

编辑:

似乎发生了两件事:

1) 您以与 Django 不兼容的方式覆盖了模型的 init 预计。您不能将用户 arg 添加到其中,您可以使用用户 kwarg 代替。 基本上,签名必须是*args,**kwargs,否则Django将无法加载 您的模型从 DB 中返回。

2) Python (IIRC) 中有一个错误会导致 list(qs) 中的错误被吞没 错误并返回空列表。这在 1.6 中已修复。

这个错误似乎在 1.6 中已修复。

https://code.djangoproject.com/ticket/20795#comment:4

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-05
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 1970-01-01
    • 2016-09-15
    • 2013-02-12
    • 2015-01-05
    相关资源
    最近更新 更多