Подтвердить что ты не робот

Счет Django RawQuerySet

Сено, я использую 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

Любые идеи?

4b9b3361

Ответ 1

Используйте функцию "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/ [Похоже, что этот сайт возможно, истек, но в Internet Archive есть: http://web.archive.org/web/20110513122309/http://djangoadvent.com/1.2/smoothing-curve/]

Ответ 2

По правде говоря, если все, что вы хотите, это общее количество записей в RawQuerySet, то непременно следует избегать добавления RawQuerySet в список.

Включение RawQuerySet в список приведет к повторению каждой записи, соответствующей запросу. Это потенциально обременительно для сервера. Вместо этого используйте count(). Это может быть достигнуто путем wrapping count() вокруг необработанного SQL, который вы использовали для создания RawQuerySet.

Я использовал это для решения проблемы:

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 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 _ ограниченным контекстом выполнения вызова? Если вы используете MOD_WSGI в Apache, означает ли это, что все потоки в процессе вызова будут совместно использовать измененное определение?

Ответ 3

Причина отсутствия "подсчета" заключается в том, что вам понадобится дополнительный запрос "count (*)" к базе данных, чтобы узнать размер вашего результирующего набора.

Так что имейте в виду, что вызов list(cars) загружает все ваши результаты в память. Это позволяет получить счетчик с len, но может быть дорогостоящей операцией, если у вас большой набор результатов.

Ответ 4

Вот улучшенное решение, основанное на user871977's:

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