2017-10-01 6 views
0

て設定します:注釈Djangoのクエリは次のようにモデルの簡単なセットを考えると逆の外部キー

class A(models.Model): 
    pass 

class B(models.Model): 
    parent = models.ForeignKey(A, related_name='b_set') 

class C(models.Model): 
    parent = models.ForeignKey(B, related_name='c_set') 

私は2つの注釈とともにAモデルのクエリセットを作成するために探しています。 1つの注釈は、A行を親として持つB行の番号である必要があります。もう1つの注釈はB行の番号を、Aオブジェクトを親とし、少なくともnタイプのCオブジェクトをc_setに持つことを示す必要があります。例として

、以下のデータベースとn = 3検討:ID 0のAオブジェクトが二Bオブジェクトを有するので、私は、フォーム[(0, 2, 1), (1, 0, 0)]の結果を得ることができるたい

Table A 
id 
0 
1 

Table B 
id parent 
0 0 
1 0 

Table C 
id parent 
0 0 
1 0 
2 1 
3 1 
4 1 

を1つは少なくとも3つの関連するオブジェクトを有する。 id 1のAオブジェクトにはBオブジェクトがないため、少なくとも3つのC行のオブジェクトも含まれていません(Bオブジェクト)。

最初の注釈は簡単です:

A.objects.annotate(annotation_1=Count('b_set')) 

私は今、設計しようとしています何秒の注釈です。私は次のようにBオブジェクトは、少なくとも一つのCを目的としているAあたりBの行数カウントするように管理しています

A.objects.annotate(annotation_2=Count('b_set__c_set__parent', distinct=True)) 

をしかし、私は他の最小関連セット・サイズでそれを行う方法を把握することはできません1つ以上。うまくいけば、ここの誰かが正しい方向に私を向けることができます。私が考えていた1つの方法は、A行の代わりにBオブジェクトをクエリに何らかの形で注釈を付けることでしたが、これはアノテートメソッドのデフォルトですが、これでリソースを見つけることができませんでした。

答えて

1

これは、Django 1.11の制限で複雑なクエリです。

from django.db.models import Count 

sub_qs = (
    C.objects 
    .values('parent') 
    .annotate(c_count=Count('id')) 
    .order_by() 
    .filter(c_count__gte=n) 
    .values('parent') 
) 
qs = B.objects.filter(id__in=sub_qs).values('parent_id').annotate(cnt=Count('id')) 
qs_map = {x['parent_id']: x['cnt'] for x in qs} 
rows = list(A.objects.annotate(annotation_1=Count('b_set'))) 
for row in rows: 
    row.annotation_2 = qs_map.get(row.id, 0) 

リストrowsは結果である:私は2つのクエリでそれを行うにしてクエリセットのようなビューで使用できる1つのリストに結果を組み合わせることにしました。より複雑なqsqueryは、比較的簡単なSQLにコンパイルされます。

>>> print(str(qs.query)) 
SELECT app_b.parent_id, COUNT(app_b.id) AS cnt 
FROM app_b 
WHERE app_b.id IN (
    SELECT U0.parent_id AS Col1 FROM app_c U0 
    GROUP BY U0.parent_id HAVING COUNT(U0.id) >= 3 
) 
GROUP BY app_b.parent_id;    -- (added white space and removed double quotes) 

この簡単な解決法は、簡単に変更してテストすることができます。


注:1つのクエリによる解決策も存在しますが、役に立たないようです。理由:SubqueryとOuterRef()が必要です。彼らは素晴らしいですが、一般的に集計からCount()は結合解決と一緒にコンパイルされたクエリでサポートされていません。サブクエリは、Djangoでコンパイルできるlookup ...__in=...に分割できますが、OuterRef()を使用することはできません。 OutRefなしで書かれていると、時間複雑度がO(n )、多くの(またはすべての)データベースバックエンドのサイズでAのテーブルになる可能性があるので、最適な入れ子になったSQLは非常に複雑です。未検証。

関連する問題