web-dev-qa-db-ja.com

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

何か案は?

19
dotty

'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/ ]

26
msanders

正直なところ、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()プロトコルに応答します。

これはパフォーマンスの点ではるかに優れていますが、1つの欠点があります。RawQuerySetを複数回使用する場合は、動的な_len _を破棄することが望ましいでしょう。 =実装。

_len _メソッドが呼び出し元の実行コンテキストによって制約されるかどうかを知っている人はいますか? ApacheでMOD_WSGIを使用している場合、これは、呼び出し元のプロセス内のすべてのスレッドが変更された定義を共有することを意味しますか?

7
user871977

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

'count'がない理由は、結果セットのサイズを知るためにデータベースへの追加の "count(*)"クエリが必要になるためです。

したがって、list(cars)を呼び出すと、すべての結果がメモリにロードされることに注意してください。これにより、lenでカウントを取得できますが、結果セットが大きい場合はコストのかかる操作になる可能性があります。

2
Scott Persinger