2012-04-19 16 views
7

モデルXYZがあり、与えられたクエリセットのフィールドa、b、および式x/yの最大値を取得する必要があります。Django式で式を集約する

これはフィールドで美しく機能します。何かのように:

>>> XYZ.all().aggregate(Max('a')) 

... {'a__max': 10} 

しかし、私は表現のためにそれを行う方法を見つけることができません。何かしよう:

>>> XYZ.all().aggregate(Max('x/y')) 

すると、エラーを与える:ような何かしようと

*** FieldError: Cannot resolve keyword 'x/y' into field. Choices are: a, b, x, y, id 

>>> XYZ.all().aggregate(Max(F('x')/F('y'))) 

はエラーを与える:

*** AttributeError: 'ExpressionNode' object has no attribute 'split' 

とにも何か:また

XYZ.all().extra(select={'z':'x/y'}).aggregate(Max('z')) 
が動作し、上記と同じエラーを与えていない:私はそれをやってい

FieldError: Cannot resolve keyword 'z' into field. Choices are: a, b, x, y, id 

1つのハックすることです。それが生成するので、実際に動作

XYZ.all().extra(select={'z':'MAX(x/y)'})[0].z 

正しいSQLですが、私はz atttributeで正しい値を取得するので、混乱しますが、正しいインスタンスではなく、最大値を持つインスタンスではありません。

もちろん、私はextra()とorder_by()で生のクエリやトリックを使うこともできますが、Djangoが集約クエリをうまくやってくれることは間違いありませんが独自のF式でも式をサポートすることはできません。

これを行う方法はありますか?

+0

あなたは集計に 'F()'オブジェクトを使用する機能は、[今後のDjangoの1.8リリースの一部]であることを知って興味がある可能性があり(https://code.djangoproject.com/ wiki/Version1.8Roadmap)。 –

答えて

6

分割後、別途

result = XYZ.aggregate(Max('x'), Max('y')) 

を最大値を取得しなければならないと思いますDjango ORM:

XYZ.objects.extra(select={'z':'x/y'}).order_by('-z')[0] 
# Or 
XYZ.objects.extra(select={'z':'x/y'}).annotate().order_by('-z')[0] 
# Or x/y=z => x=y*z 
XYZ.objects.filter(x=models.F('y') * XYZ.objects.extra(select={'z':'MAX(x/y)'})[0].z)[0] 

バージョン

XYZ.all().extra(select={'z':'MAX(x/y)'})[0].z 

MAX関数がこのよう返さクエリセット内のすべてのインスタンスが同じ値を持つことになり、何GROUP BYがない場合、全ての行の間で評価されるため、正しいX、Y及びインスタンスを持っていませんzMAX(x/y)とする。

+0

右ですが、目的は最大値を得ることです。 XYZ.all()。aggregate(Max( 'a'))からの戻り値は、それを含むインスタンスではありません。 extra-selectのバージョンが最も近いバージョンです。正しいインスタンスを返さないのは混乱する副作用ですが、正しい値を返します。私がオープニングメッセージで述べたように、私はextraとorder_byのソリューションを知っていますが、シングルパスではなく完全な並べ替えが必要なため、これらは受け入れられません。 Djangoが単一のフィールドではなく式ではなくMax集約をサポートすることはあまり意味がありません。 –

+0

@pjwerneckあなたが呼び出す混乱する副作用の理由は、私の答えの最後の段落で説明されています。あなたが最大値だけを必要とするならば、 'XYZ.objects.extra(select = {'z': 'MAX(x/y)'})[0] .z'で十分です、order_byはありません。あるいは直接 'cursor.execute( 'XYZからSELECT MAX(x/y)')'を実行することもできます。私はDjangoが単一フィールドのIMOをサポートするよりも劇的に難しいかもしれないので、Djangoは集計式を提供していないとあなたに同意します。 – okm

-3

は、私が何をしたいことは、実際にこのよう

SELECT x/y, * FROM XYZ ORDER BY x/y DESC LIMIT 1; 
# Or more verbose version of the #1 
SELECT x/y, id, a, b, x, y FROM XYZ GROUP BY x/y, id, a, b, x, y ORDER BY x/y DESC LIMIT 1; 
# Or 
SELECT * FROM XYZ WHERE x/y = (SELECT MAX(x/y) FROM XYZ) LIMIT 1; 

である、あなたは二つのフィールド

SQLで
result['x__max'] \ result['y__max'] 
+0

それは意味をなさない。たとえmax xとyのペアを持つ行を返すとしても、必ずしもmax x/yとは限りません。例えば、Max(x)/ Max(y)行は69/16 = 4で、max(x/y)は8/1 = 8です。 –

2

F()オブジェクトを使用している例では、Django 1以降は正常に動作します。8:1.8より低いバージョンの

Book.objects.all().aggregate(price_per_page=Sum(F('price')/F('pages')) 
+0

お役立ち情報ありがとう! –

0

あなたがこの(文書化されていない)方法で同じことを達成することができます

XYZ.all().aggregate(Max(F('x')/F('y'))) 

Django aggregation cheat sheetSum()F()オブジェクトと凝集を示してスニペットがあります。

Book.objects.all().aggregate(price_per_page=Sum('price_per_page', 
               field='book_price/book_pages')) 

これはPostgresのために働く、私は、MySQLのことは知りません。

出典:Django Aggregation: Summation of Multiplication of two fields