2

だから私はこれに相当するモデルを持っています(非常に単純化されています、明らかに):

class Mystery(models.Model):
    name = models.CharField(max_length=100)

class Character(models.Model):
    mystery = models.ForeignKey(Mystery, related_name="characters")
    required = models.BooleanField(default=True)

基本的に、各ミステリーには、ストーリーに不可欠であるかどうかに関係なく、多くのキャラクターがいます。ミステリーを上演できる俳優の最小数は、そのミステリーに必要なキャラクターの数です。最大数はミステリーの合計文字数です。

今、私は特定の数の俳優が演じることができる謎を探そうとしています。Djangoのフィルタリングおよび注釈機能が機能する方法を使用すると、それは十分に簡単に思えました。結局のところ、これらのクエリは両方とも正常に機能します。

# Returns mystery objects with at least x characters in all
Mystery.objects.annotate(max_actors=Count('characters', distinct=True)).filter(max_actors__gte=x)

# Returns mystery objects with no more than x required characters
Mystery.objects.filter(characters__required=True).annotate(min_actors=Count('characters', distinct=True)).filter(min_actors__lte=x)

しかし、私が2つを組み合わせようとすると...

Mystery.objects.annotate(max_actors=Count('characters', distinct=True)).filter(characters__required=True).annotate(min_actors=Count('characters', distinct=True)).filter(min_actors__lte=x, max_actors__gte=x)

...動作しません。min_actorsとmax_actorsの両方に、最大数のアクターが含まれています。実行されている実際のクエリの関連部分は次のようになります。

SELECT `mysteries_mystery`.`id`,
    `mysteries_mystery`.`name`,
    COUNT(DISTINCT `mysteries_character`.`id`) AS `max_actors`,
    COUNT(DISTINCT `mysteries_character`.`id`) AS `min_actors`
FROM `mysteries_mystery`
    LEFT OUTER JOIN `mysteries_character` ON (`mysteries_mystery`.`id` = `mysteries_character`.`mystery_id`)
    INNER JOIN `mysteries_character` T5 ON (`mysteries_mystery`.`id` = T5.`mystery_id`)
WHERE T5.`required` = True
GROUP BY `mysteries_mystery`.`id`, `mysteries_mystery`.`name`

...これにより、Djangoが文字テーブルに2番目の結合を正常に作成している間(テーブルの2番目のコピーはT5にエイリアスされています)、そのテーブルは実際にはどこでも使用されておらず、両方のカウントが使用されていることがわかります非エイリアスバージョンから選択されます。これにより、どちらの場合も明らかに同じ結果が得られます。

句を使用しextraてT5から選択しようとしても、出力クエリを調べると2番目の文字テーブルがT5にエイリアシングしていることがわかっているにもかかわらず、T5のようなテーブルはないと言われます。extra句を使用してこれを行う別の試みは、次のようになりました。

Mystery.objects.annotate(max_actors=Count('characters', distinct=True)).extra(select={'min_actors': "SELECT COUNT(*) FROM mysteries_character WHERE required = True AND mystery_id = mysteries_mystery.id"}).extra(where=["`min_actors` <= %s", "`max_actors` >= %s"], params=[x, x])

しかし、少なくともMySQLでは、WHERE句で計算フィールドを使用できないため、これは機能しませんでした。HAVINGを使用できたとしても、残念ながら、Djangoの.extra()では、 HAVINGパラメーターを設定できません。

DjangoのORMに私がやりたいことをさせる方法はありますか?

4

2 に答える 2

0

Count()を組み合わせるのはどうですか?

Mystery.objects.annotate(max_actors=Count('characters', distinct=True),min_actors=Count('characters', distinct=True)).filter(characters__required=True).filter(min_actors__lte=x, max_actors__gte=x)

これは私にとってはうまくいくようですが、私はあなたの正確なモデルでそれをテストしませんでした。

于 2013-01-30T05:11:43.290 に答える
0

提案された解決策がないまま数週間が経過したので、答えを探している可能性のある他の人のために、私が最終的にそれを行った方法を次に示します。

Mystery.objects.annotate(max_actors=Count('characters', distinct=True)).filter(max_actors__gte=x, id__in=Mystery.objects.filter(characters__required=True).annotate(min_actors=Count('characters', distinct=True)).filter(min_actors__lte=x).values('id'))

つまり、最初のカウントと、2 番目のカウントでフィルター処理する明示的なサブクエリ内の ID に一致する ID でフィルター処理します。ちょっと不格好ですが、私の目的には十分に機能します。

于 2013-02-15T14:23:55.983 に答える