7

2 つのモデル (簡易版) があります。

class Contestant(models.Model):
    email = models.EmailField(max_length=255, unique=True)
    # plus some other fields

    @property
    def total_points(self):
        return self.points.aggregate(total=Sum('value'))['total'] or 0

class Points(models.Model):
    contestant = models.ForeignKey(Contestant, related_name='points')
    value = models.PositiveIntegerField()
    # plus some other fields which determine based on what we
    # awarded ``Points.value``

競技者のリストとそのtotal_points値を表示すると、結果ごとに追加のクエリが発生します。つまり、次のクエリが実行されます。

  1. 出場者のリストを取得する
  2. 第 1 競技者のフェッチtotal_points
  3. total_points2 番目の出場者の値をフェッチ

次のようにクエリセットを変更して、データをプリフェッチしようとしました。

Contestant.objects.filter(...).prefetch_related('points')

...ただし、機能しても、競技者をリストするときにプリフェッチされたデータは使用されません (そのため、各結果は引き続きtotal_points 個別のクエリでフェッチしようとします)。

次のことは可能ですか。

  • 個々のモデル オブジェクトにデータを入力するときに、@property フィールドにプリフェッチされた値を使用するように ORM に指示します (たとえば、Contestant.total_points@property メソッド内のプリフェッチされた値にアクセスします)。
  • または(上記の例とは対照的に)別の方法でそれらをプリフェッチしますか?
  • または、まったく異なるアプローチを使用して同じ結果を達成しますか?

tastypie(問題があれば、結果を にリストしています。)

ありがとうございました。

4

1 に答える 1

11

各項目に集計値を追加することが目的の場合はannotate、 の代わりに ,を使用する必要がありaggregateます。

例 (単純なクエリ、追加のメソッドは必要ありません):

Contestant.objects.filter(...).annotate(total_points=Sum('points__value'))

本当にこのコードをクエリから除外したい場合: 可能ですが、モデル メソッドはこれを行う正しい方法ではありません。モデルのメソッドは、単一インスタンスの操作用です。QuerySet 全体で何かをしたい場合は、代わりにORM Managerを使用してください。

Manager の場合、これは次のようになります。

class TotalPointsManager(models.Manager):
    def get_queryset(self):
        return super(TotalPointsManager, self).get_queryset().annotate(total_points=Sum('points__value'))

class Contestant(models.Model):
    email = models.EmailField(max_length=255, unique=True)

    objects = TotalPointsManager() # You are overriding the default manager!

そして、通常どおりクエリを作成します(ドロップできますprefetch_related):

Contestant.objects.filter(...)

...そして、total_pointsフィールドはすべてのオブジェクトで「魔法のように」利用可能になります。

于 2013-10-01T13:43:01.497 に答える