Счет Django RawQuerySet

19

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

Любые идеи?

    
задан dotty 23.02.2010 в 11:52
источник

4 ответа

24

Используйте функцию «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 () по адресу: Ссылка [ Похоже, что этот сайт, возможно, истек, но в Internet Archive есть он: Ссылка ]

    
ответ дан msanders 23.02.2010 в 12:09
источник
  • Получение этого объекта ошибки типа «RawQuerySet» не имеет len () –  dotty 23.02.2010 в 12:25
  • len (список (автомобили)), похоже, работает после того, как я передал объект в список –  dotty 23.02.2010 в 12:30
  • ОК, я обновил ответ соответственно. –  msanders 23.02.2010 в 12:39
  • Спасибо за msanders. –  dotty 23.02.2010 в 12:56
  • Мне также интересно, можно ли оптимизировать это, используя «SELECT COUNT (*)» (при условии, что вам нужно только количество строк, а не самих строк). В настоящее время у меня нет 1.2, поэтому не могу попробовать. –  msanders 23.02.2010 в 12:59
Показать остальные комментарии
6

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

Включение RawQuerySet в список приведет к повторению каждой записи, соответствующей запросу. Это потенциально обременительно для сервера. Вместо этого используйте count (). Это может быть достигнуто путем округления 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'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 _ ограниченным контекстом выполнения вызывающего? Если вы используете MOD_WSGI в Apache, означает ли это, что все потоки процесса вызывающего пользователя будут совместно использовать измененное определение?

    
ответ дан user871977 12.08.2013 в 05:21
источник
3

Вот улучшенное решение, основанное на 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))
    
ответ дан caot 17.03.2016 в 20:06
источник
  • Вместо того, чтобы вручную вводить params в строку sql, нужно просто передать их в cursor.execute (sql, self.params) –  serg 12.02.2018 в 21:51
  • @serg вызов cursor.execute (sql, [params]), если на основе Django doc по адресу https://docs.djangoproject.com/en/2.0/topics/db/sql/#executing-custom-sql-directly , Благодаря! –  caot 14.02.2018 в 14:46
2

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

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

    
ответ дан Scott Persinger 07.02.2013 в 23:06
источник
  • Это работает только в том случае, если запрос возвращает хотя бы одну строку, иначе счет не будет где угодно :-) –  Mariano Suárez-Álvarez 19.06.2014 в 21:47