2

の対象は、単純化された私のモデルのバージョンでカウント彼が所有するFlightオブジェクトのフィールドには、Flightというオブジェクトの対応する番号が付いています。はここで条件

たとえば、ユーザーが3便でご利用されているとします。LAX-LHRLHR-CDG、およびCDG-JFKです。その後、私は次のオブジェクトを返すクエリたいと思います:上記で

[LHR, id__count=2}, {CDG, id__count=2}, {LAX, id__count=1}, {JFK, id__count=1}] 

を、3つの文字コードはAirportオブジェクトまたはすべてのフィールドに立っています。

一般的に、User sおよび十Airport sおよびFlight秒の何千もの何千ものがあるかもしれないので、私は、好ましくは、単一のデータベースクエリで、forループやif文との明白な解決策よりも効率的なものを求めています。初期filterが唯一の彼のフライトでどこかに表示され、それらの空港を保つため

Airport.objects.filter(
    Q(origins__owner=user) | Q(destinations__owner=user) 
) 
.distinct() 
.annotate(
    id__count=Count('origins', distinct=True) + Count('destinations', distinct=True) 
).order_by('-id__count') 

これは、1人のユーザーにのみ完璧に動作します:

私の現在の進行状況は、このクエリです。しかし、カウントにはすべてのユーザーのフライトが含まれているため、複数のユーザーがいる場合は明らかに失敗します。 owner=userここで、これらのFlightオブジェクトは、owner=userです。userは、特定のUserオブジェクトです。


編集:this page in the Djnago documentationを読んだ後、必要に応じて第一のフィルタを置くことは、この仕事をしなければならないようです。しかし、少なくとも私はQオブジェクトを使用していません。私は次の非常に混乱した結果を発見しました。

私はこのクエリを使用する場合、すなわち唯一の起源を見て、それが動作し、num_originsフィールドが指定されたuserに属するものだけ便をカウント:

Airport.objects.filter(origins__owner=user).annotate(num_origins=Count('origins')) 

(これはまさに私ではありませんカウントのみ原点特定Airportですが、それは正しくUser Sをフィルタリングしない航空券を含むので

)。必要しかし、私は何もしないときにはと組み合わせるか、2つのQオブジェクトを単一のフィルタを交換する、すなわち

Airport.objects.filter(Q(origins__owner=user) | Q(destinations__owner=user)).annotate(num_origins=Count('origins')) 

現在、すべてのユーザーに属するフライトをカウントします。注釈は、Qオブジェクトを使用するときにフィルタについて「忘れる」ようです。ここで何が起こっているのですか?

+0

あなたはフライトオブジェクトの所有者キーの関連名を追加していないのはなぜ?その後、すべてのユーザーのフライトを取得し、別々のフライトカウントのみが表示されるように個別に適用することができます。 –

+0

@ÇağatayBarınオーナーフィールドに関連する名前を追加しましたが、それを使用する方法はまだわかりません。もう少し説明できますか?ありがとう。 – jc315

答えて

2

私はあなたが条件式でこれを実現することができると思います。それは実際に代わりにこれを実行する方が効率的かもしれません(おそらく見つけるために結果のSQLクエリを検査する必要があります):

Airport.objects.annotate(
    num_origins=Count(
     Case(When(Q(origin__owner=user), then=1, else=0)), 
    ), 
    num_destinations=Count(
     Case(When(Q(destination__owner=user), then=1, else=0)), 
    ) 
).filter(Q(num_origins__gt=0) | Q(num_destinations__gt=0)) 

はすなわち、すべてのフライトに注釈を付け、その後、カウントが0だったものを除外する。

num_originsnum_destinationsをPythonで追加できます。

あなたがDjangoの2を使用している場合は、Countにフィルタ引数を渡すことができますので、それはまだ簡単です:

Airport.objects.annotate(
    num_origins=Count('origins', filter=Q(origin__owner=user), distinct=True), 
    num_destinations=Count('destinations', filter=Q(destination__owner=user), disctinct=True) 
).filter(Q(num_origins__gt=0) | Q(num_destinations__gt=0)) 
+0

答えをありがとう、しかし私は働くための最初のオプションを得ることができません。私はあなたが 'else = 0'ではなく' default = 0'を意味していたと思いますが、それでもカウントは間違っていて、ユーザフィルタを無視しているようです。これはデータベースの問題でしょうか?私は現在SQLiteを使用していますが、私はそれを維持しようと考えていましたが、もし役立つなら、私はMySQLに移ることができました。いずれにしても、次に私はDjango 2.0にアップグレードし、2番目のオプションを使用してみます。 – jc315

+0

私はDjango 2.0にアップグレードしました.2番目のオプションが機能します!しかし、いくつかのタイプミスがあります。「Count」の最初の引数は「origin」と「destinations」でなければなりません。また、「distinct = True」も含める必要があります。これらの間違いを訂正すれば、私はあなたの答えを受け入れます。 – jc315

+0

あなたのコメントに基づいて編集しました。最初のアプローチがうまくいかなかった理由もあまり確かではありません。 SQLiteは、コードが動作していても、本番環境での使用はお勧めしません。 PostgresやMySQLを使う方が良い。 – solarissmoke

0

あなたはこれを試すことができますか?私はシェルでそれをテストしなかったので、私は 'distinct_flights'リスト構造については分かりませんが、あなたはそのアイデアを得るでしょう。

from django.db.models import Case, When 

Airport.objects.filter(
    Q(origins__owner=user) | Q(destinations__owner=user) 
).annotate(
    num_origins=Count(
     Case(When(Q(origin__owner=user), then=1, else=0)), 
    ), 
    num_destinations=Count(
     Case(When(Q(destination__owner=user), then=1, else=0)), 
    ) 
) 

注意をWhen句を使用すると、最初に行うのと同じフィルタを繰り返していること:

# This is all of the distinct flights of your users. 
distinct_flights = Flight.objects.filter(owner__in=[user1.id, user2.id]).distinct().values_list('origin','destination') 

# This is all of the airports included in the flights above. 
Airport.objects.filter(
    Q(origins__in=distinct_flights['origin'])|| 
    Q(destination__in=distinct_flights['destination']) 
) 

# The rest is annotation from those airports as you did before. You can annotate it on the above query again. 
+0

ありがとうございますが、この種の構造はすべてのユーザーのフライトをカウントしています。フィルターでQオブジェクトを使用しないと、それは機能し、注釈は指定されたユーザーのフライトだけをカウントします。しかし、Qオブジェクトをorと組み合わせて使用​​すると、アノテーションはすべてのユーザーのフライトをカウントします。私の編集を参照してください。 – jc315