2017-11-22 21 views
1

私は、外部キーテーブルに基づいて基本的な「お気に入り」システムを実装しようとしています。ブール注釈により重複が発生しましたか?

のは、私は、次の単純なモデルを持っているとしましょう:

class Item(models.Model) 
    id = models.IntegerField() 

class User(models.Model) 
    id = models.IntegerField() 

class UserFavourites(models.Model) 
    user = models.ForeignKey(User, related_name='user_for_fav_rel') 
    item = models.ForeignKey(Item, related_name='item_for_fav_rel') 

は、今私は、彼らは、ユーザーがお気に入りとしてマークされているかどうかを確認する項目について、次のクエリセットを生成します。

queryset = Item.objects.all() 

USER_ID = request.user.id 

queryset = queryset.annotate(
    favourite=Case(
     When(
      item_for_fav_rel__user__id=USER_ID, 
      then=Value('True') 
     ), 
     default=Value('False'), 
     output_field=BooleanField() 
    ) 
) 

すべてこの作品の偉大な作品ですが、応答では、アイテムが実際に寄付されている場合、私はその特定のアイテムの複製をクエリーセットに受け取ります。どのようにこれを避けるためのアイデア?

結果として得られるSQLクエリ(私が考える最低限の例にダウンし、編集...)

SELECT 
    "core_item"."id", 
    CASE 
     WHEN "core_userfavourites"."user_id" = 1 THEN True 
     ELSE False 
    END AS "favourite" 
FROM "core_item" 
LEFT OUTER JOIN "core_userfavourites" 
    ON ("core_item"."id" = "core_userfavourites"."item_id") 
+0

生成するSQLクエリは何ですか? –

+0

質問の編集を見る... – Eric

+0

'all()'と 'annotate()'を使う特別な理由はありますか? – scharette

答えて

1

問題は、あなたがcore_itemとcore_userfavoritesの組み合わせごとに1行を取得しているです。そこには生のSQLなしにしようとしているのかを正確に行うための方法であると思われるが、幸いDjangoはごく最近(1.11)は、あなたがここに

from django.db.models.expressions import OuterRef, Subquery 

queryset = Item.objects.all() 

USER_ID = request.user.id 
user_favorites = UserFavourites.objects.filter(
    user_id=USER_ID, 
    item_id=OuterRef('id') 
)[:1].values('user_id') 

queryset = queryset.annotate(user_favorite=Subquery(user_favorites)) 

この意志を使用することができますサブクエリ条項を書き込む機能を追加していませんuser_favoriteフィールドにuser_idを入力します(ユーザーがお気に入りの場合はNone)。

基本的に、関連テーブルから任意の値を選択するためのサブクエリを作成しています。

+0

ありがとうVinay!私は今夜​​後にこれを試してみる。質問:なぜサブクエリをスライスしていますか?これはブール結果に適応可能ですか? – Eric

+0

サブクエリが複数の行を返すと、クエリは失敗します。この場合、必要ないかもしれません。元の式のように、サブクエリをラップすることで、これをブール値に変換できます。 NoneはFalseに評価され、user_idの他の値はTrueと評価されますが、それは価値があるかどうかはわかりません。私はそれが少し醜いことを認めます。 exists()が実行をトリガーして直ちに値を返すのではなく、クエリーセットまたは別の連鎖可能なオブジェクトを返すことができればいいです。 –

+1

もう一度ありがとう!これは素晴らしいです。私はDjango Rest Frameworkの一部として 'item'のシリアライザで真/偽にすることができました。 – Eric

関連する問題