【问题标题】:django giving TypeError when using len() on a QuerySetdjango 在 QuerySet 上使用 len() 时给出 TypeError
【发布时间】:2011-06-10 23:06:30
【问题描述】:

我正在编写一个非常简单的应用程序来存储足球比赛的结果,但我遇到了以下问题。运行其中一个单元测试时,以下代码:

listCompetition = Competition.objects.filter(compId=competitionId)
if len(listCompetition) == 0:
   #some code here
else:
   #some code here

给出以下错误:

File "C:\Users\admin\workspace\project\src\bla\bla\module.py", line 222, in getMatches
   if len(listCompetition) == 0:
File "C:\Python27\lib\site-packages\django\db\models\query.py", line 82, in __len__
   self._result_cache = list(self.iterator())
File "C:\Python27\lib\site-packages\django\db\models\query.py", line 286, in iterator
   obj = model(*row[index_start:aggregate_start])
TypeError: __init__() takes exactly 3 arguments (4 given)

但是,如果我将第一行代码替换为:

listCompetition = list(Competition.objects.filter(compId=competitionId))

然后它工作得很好。为什么它会以这种奇怪的方式表现?如果我在 Competition 类的构造函数中只定义了两个,那么 Django 怎么会传递 4 个参数?如果有帮助,这里是竞赛类的模型定义:

class Competicion(MultiName):
   def __init__(self, canonicalName, compId):
      super(Competition, self).__init__(canonicalName, compId)

class MultiName(models.Model):
   entId = models.CharField(null=True, max_length=25); 
   canonicalName = models.CharField(max_length=50, primary_key=True);

   def __init__(self, canonicalName, entId=None):
      super(MultiName, self).__init__()
      self.canonicalName = canonicalName;
      self.entId = entId;

非常感谢。

【问题讨论】:

    标签: python django


    【解决方案1】:

    简单:在第一种情况下,您得到的是一个查询集对象,而不是一个列表。查询集是迭代器。列表对象也是迭代器,但迭代器不是列表。

    Django 这样做是为了优化内存和性能:数据库保留集合,并且每次您从查询集中请求一个对象时,Django 一次读取一个响应。使用list() 会使 Django 读取整个响应并将其打包到一个列表对象中。如果返回集非常大,这可能会出现问题。

    要知道查询集有多大,请改用Queryset.count() 方法。

    【讨论】:

    • len() 适用于迭代器...(尝试len(xrange(10)))——但是是的,使用.count() 是正确的方法。
    • 是的,但是 len() 会导致列表强制。这是非常糟糕的形式。不过,您是对的,他的 __init__() 写得不正确,完全没有必要。
    【解决方案2】:

    代替

    if len(listCompetition) == 0:
    

    使用

    if listCompetition.count() == 0:
    

    或许

    if not listCompetition.exists():
    

    【讨论】:

    • 他的例外是错误地覆盖__init__。当然使用.count() 会绕过这个问题,但是一旦他尝试检索模型实例,就会引发相同的异常。
    【解决方案3】:

    它被破坏了,因为您在覆盖 __init__ 时破坏了 Django 模型使用的约定。你为什么要实施__init__?你正在实施的是……已经实施了。

    您似乎对如何使用 Django 做出了毫无根据的假设。我建议您在继续之前阅读tutorial

    要修复您的代码,请将其更改为以下内容:

    class Competition(MultiName):
        def __init__(self, *args, **kwargs):
            if "compId" in kwargs:
                kwargs["entId"] = kwargs.pop("compId")
            super(Competition, self).__init__(*args, **kwargs)
    
    class MultiName(models.Model):
        entId = models.CharField(null=True, max_length=25); 
        canonicalName = models.CharField(max_length=50, primary_key=True);
    

    有很多 great documentation for models 涵盖了这些内容。

    【讨论】:

    • 布拉德利:不应该是 __init__(self, *args, **kwargs) 吗?另外,请参阅上面的回答:他更大的问题是不理解 Queryset 是迭代器,而不是列表。
    • 是的,很好。我想说他更大的问题是以某种方式覆盖__init__,使他无法检索模型实例。 len() vs .count() 真的只是一个性能问题,两者都可以工作。
    • 你完全正确,我应该创建我的对象而不覆盖__init__ ,调用instance = Competition(canonicalName='name', entId='id') 会容易得多。我的背景是 Java,我试图在 Java 中做类似的事情(失败),但现在我意识到这不是 Django 的方式。非常感谢您的回答。
    猜你喜欢
    • 2012-12-28
    • 2020-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-26
    • 2012-12-26
    • 2012-01-01
    相关资源
    最近更新 更多