Django 计数 RawQuerySet

21 投票
4 回答
32932 浏览
提问于 2025-04-15 19:37

嘿,我在用Django 1.2,想知道怎么从一个原始查询集(RawQuerySet)中计算行数。

传统的.count()方法不管用。

这是我的查询:

query = "SELECT *, ((ACOS(SIN(%s * PI() / 180) * SIN(lat * PI() / 180) + COS(%s * PI() / 180) * COS(lat * PI() / 180) * COS((%s - lon) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS distance FROM app_car WHERE price BETWEEN %s AND %s HAVING distance<=%s ORDER BY distance ASC"

cars = Car.objects.raw(query, [lat, lat, lon, min_price, max_price, miles])

return HttpResponse( cars )

它返回的是:

Car_Deferred_model_id_user_id object

有什么想法吗?

4 个回答

7

这是一个改进后的解决方案,基于用户871977的内容:

from django.db import connection

def get_len(rawqueryset):
    def __len__(self):
        params = ["""'%s'""" % p for p in self.params]
        sql = 'SELECT COUNT(*) FROM (' + (rawqueryset.raw_query % tuple(params)) + ') B;'
        cursor = connection.cursor()
        cursor.execute(sql)
        row = cursor.fetchone()
        return row[0]
    return __len__

rawqueryset = .... # a RawQuerySet instance
setattr(type(rawqueryset), '__len__', get_len(rawqueryset))
8

说实话,如果你只是想知道RawQuerySet里有多少条记录,那就最好不要把RawQuerySet转成列表。

把RawQuerySet转成列表会逐条检查每一条符合条件的记录,这样会给服务器带来负担。你可以使用count()来代替。只需要在你用来生成RawQuerySet的原始SQL语句外面加上count()就可以了。

我用这个方法解决了问题:

def add_len_protocol_to_raw_sql_query( query ):
    """
    Adds/Overrides a dynamic implementation of the length protocol to the definition of RawQuerySet for the remainder of this thread's lifespan
    """
    from django.db.models.query import RawQuerySet
    def __len__( self ):
        from django.db import connection
        sql = 'SELECT COUNT(*) FROM (' + query + ') B;'
        cursor = connection.cursor()
        cursor.execute( sql )
        row = cursor.fetchone()
        return row[ 0 ]
    setattr( RawQuerySet, '__len__', __len__ )
query = 'SELECT * FROM A_TABLE_OF_MINE'
add_len_protocol_to_raw_sql_query( query )

这样做会动态修改RawQuerySet,让它能响应len()这个功能。

这样在性能上要好得多,不过有一个潜在的缺点:如果你多次使用RawQuerySet,可能就需要放弃这个动态的_len_实现。

你们知道_len_方法会受到调用者执行环境的限制吗?如果在Apache上使用MOD_WSGI,这是否意味着调用者进程中的所有线程都会共享这个修改后的定义?

31

使用 'len()' 函数。这会返回:

query = "SELECT *, ((ACOS(SIN(%s * PI() / 180) * SIN(lat * PI() / 180) + COS(%s * PI() / 180) * COS(lat * PI() / 180) * COS((%s - lon) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS distance FROM app_car WHERE price BETWEEN %s AND %s HAVING distance<=%s ORDER BY distance ASC"

cars = Car.objects.raw(query, [lat, lat, lon, min_price, max_price, miles])

return HttpResponse(len(list(cars))

顺便提一下,关于 Django 1.2 的 Model.objects.raw() 方法,有一些有用的信息可以参考:http://djangoadvent.com/1.2/smoothing-curve/ [看起来那个网站可能已经过期了,但互联网档案馆保存了它:http://web.archive.org/web/20110513122309/http://djangoadvent.com/1.2/smoothing-curve/ ]

撰写回答