Django在对QuerySet使用len()时出现TypeError

7 投票
3 回答
6957 浏览
提问于 2025-04-16 19:21

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

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))

那么它就能正常工作了。为什么会这样呢?为什么Django会传递4个参数,而我在Competition类的构造函数里只定义了两个参数?如果有帮助的话,这里是Competition类的模型定义:

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;

非常感谢。

3 个回答

9

不要用

if len(listCompetition) == 0:

而是用

if listCompetition.count() == 0:

或者也可以用

if not listCompetition.exists():
11

简单来说,在你的第一个例子中,你得到的是一个查询集对象,而不是一个列表。查询集是可以逐个访问的对象,类似于迭代器。列表也是可以逐个访问的对象,但迭代器并不等同于列表。

Django这样做是为了优化内存和性能:数据库会保持这个数据集,而Django每次你从查询集中请求一个对象时,都会逐个读取响应的数据。如果你使用list(),Django就会一次性读取所有的响应数据,并把它们放进一个列表里。如果返回的数据集非常大,这样做可能会造成问题。

如果你想知道一个查询集有多大,可以使用Queryset.count()这个方法。

3

你的代码出问题了,因为你在重写 __init__ 方法时,打破了 Django 模型的使用规则。你为什么要重写 __init__ 呢?其实你想做的事情,Django 已经帮你实现好了。

你似乎对如何使用 Django 有一些不切实际的想法。我建议你在继续之前,先看看这个 教程

要修复你的代码,可以改成下面这样:

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);

关于模型的内容,有很多很棒的 文档 可以参考。

撰写回答