Django在对QuerySet使用len()时出现TypeError
我正在编写一个非常简单的应用程序,用来存储足球比赛的结果,但遇到了一个问题。在运行其中一个单元测试时,以下代码:
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);
关于模型的内容,有很多很棒的 文档 可以参考。