web-dev-qa-db-ja.com

Django booleanフィールドを使用したクエリ注釈

店頭に製品があるProductモデルと、0個以上の画像を持つことができる製品の画像を持つProductImagesテーブルがあるとします。簡単な例を示します。

class Product(models.Model):
  product_name = models.CharField(max_length=255)
  # ...

class ProductImage(models.Model):
  product = models.ForeignKey(Product, related_name='images')
  image_file = models.CharField(max_length=255)
  # ...

製品の検索結果を表示するとき、関連する画像がある製品に優先順位を付けたいと思います。画像の数を簡単に取得できます。

from Django.db.models import Count
Product.objects.annotate(image_count=Count('images'))

しかし、それは実際には私が望むものではありません。製品に1つ以上の画像があるかどうかを示すブールフィールドhave_imagesで注釈を付けたいので、それでソートできます。

Product.objects.annotate(have_images=(?????)).order_by('-have_images', 'product_name')

どうやってやるの?ありがとう!

30
Brett Gmoser


最終的にDjango 1.8の新しい 条件式 :を使用してこれを行う方法を見つけました。

from Django.db.models import Case, When, Value, IntegerField
q = (
    Product.objects
           .filter(...)
           .annotate(image_count=Count('images'))
           .annotate(
               have_images=Case(
                   When(image_count__gt=0,
                        then=Value(1)),
                   default=Value(0),
                   output_field=IntegerField()))
           .order_by('-have_images')
)

それが、1.7から1.8にアップグレードするインセンティブをようやく見つけた方法です。

46
Brett Gmoser

条件式を使用し、outputfieldをBooleanFieldにキャストします

Product.objects.annotate(image_count=Count('images')).annotate(has_image=Case(When(image_count=0, then=Value(False)), default=Value(True), output_field=BooleanField())).order_by('-has_image')
4
Noufal Valapra

Django 1.11から)Existsを使用することができます。以下の例は Exists documentation からのものです。

>>> from Django.db.models import Exists, OuterRef
>>> from datetime import timedelta
>>> from Django.utils import timezone
>>> one_day_ago = timezone.now() - timedelta(days=1)
>>> recent_comments = Comment.objects.filter(
...     post=OuterRef('pk'),
...     created_at__gte=one_day_ago,
... )
>>> Post.objects.annotate(recent_comment=Exists(recent_comments))
3
proxy

extra に関するドキュメントを読む

qs = Product.objects.extra(select={'has_images': 'CASE WHEN images IS NOT NULL THEN 1 ELSE 0 END' })

動作確認済み

だが order_byまたはこのフィールドのwhere(filter)は私には向かない(Django 1.8)0o:

Extra()でインクルードした新しいフィールドまたはテーブルの一部を使用して結果のクエリセットを順序付ける必要がある場合は、order_byパラメーターをextra()に使用して、一連の文字列を渡します。これらの文字列は、table_name.column_nameの形式のモデルフィールド(クエリセットの通常のorder_by()メソッドのように)、またはextra()のselectパラメーターで指定した列のエイリアスである必要があります。

qs = qs.extra(order_by = ['-has_images'])

qs = qs.extra(where = ['has_images=1'])

FieldError:キーワード「has_images」をフィールドに解決できません。

https://code.djangoproject.com/ticket/19434 がまだ開いていることがわかりました。

したがって、私のような問題が発生した場合は、 raw を使用できます

1
madzohan

パフォーマンスが重要な場合は、hasPicturesブールフィールドを追加することをお勧めします(editable=False

次に、ProductImageモデル信号 (またはsaveおよびdeleteメソッドを上書き)で正しい値を維持します

利点:

  • インデックスフレンドリー。
  • よりよい性能。結合を避けます。
  • データベースに依存しない。
  • コーディングすると、Djangoスキルが次のレベルに上がります。
1
dani herrera

いくつかのフィルターで存在に注釈を付ける必要がある場合、Sum注釈を使用できます。たとえば、次のimagesにGIFがあるかどうかに注釈を付けます。

Product.objects.filter(
).annotate(
    animated_images=Sum(
        Case(
             When(images__image_file__endswith='gif', then=Value(1)),
             default=Value(0),
             output_field=IntegerField()
        )
    )
)

これは実際にそれらをカウントしますが、Pythonic if product.animated_images:はブール値と同じように機能します。

0
Ivan Klass