Django count 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 ответа

26

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

    
ответ дан 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
Показать остальные комментарии
7

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

Приведение RawQuerySet в список будет повторять каждую запись, соответствующую запросу. Это потенциально обременительно для сервера. Вместо этого используйте 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:

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