23

私は次のようにプレーヤーが毎週プレイした回数のカウントを取得しようとしています:

player.game_objects.extra(
    select={'week': 'WEEK(`games_game`.`date`)'}
).aggregate(count=Count('week'))

しかし、Djangoはそれを不平を言います

FieldError: Cannot resolve keyword 'week' into field. Choices are: <lists model fields>

私はこのように生のSQLでそれを行うことができます

SELECT WEEK(date) as week, COUNT(WEEK(date)) as count FROM games_game
WHERE player_id = 3
GROUP BY week

Djangoで生のSQLを実行せずにこれを行う良い方法はありますか?

4

2 に答える 2

15

カスタム集計関数を使用して、クエリを生成できます。

WEEK_FUNC = 'STRFTIME("%%%%W", %s)' # use 'WEEK(%s)' for mysql

class WeekCountAggregate(models.sql.aggregates.Aggregate):
    is_ordinal = True
    sql_function = 'WEEK' # unused
    sql_template = "COUNT(%s)" % (WEEK_FUNC.replace('%%', '%%%%') % '%(field)s')

class WeekCount(models.aggregates.Aggregate):
    name = 'Week'
    def add_to_query(self, query, alias, col, source, is_summary):
        query.aggregates[alias] = WeekCountAggregate(col, source=source, 
            is_summary=is_summary, **self.extra)


>>> game_objects.extra(select={'week': WEEK_FUNC % '"games_game"."date"'}).values('week').annotate(count=WeekCount('pk'))

ただし、このAPIは文書化されておらず、すでに生のSQLが必要なため、生のクエリを使用する方がよい場合があります。

于 2011-03-04T15:13:00.743 に答える
4

これは、問題の例と理想的でない回避策です。このサンプルモデルを見てください:

class Rating(models.Model):
    RATING_CHOICES = (
        (1, '1'),
        (2, '2'),
        (3, '3'),
        (4, '4'),
        (5, '5'),
    )
    rating = models.PositiveIntegerField(choices=RATING_CHOICES)
    rater = models.ForeignKey('User', related_name='ratings_given')
    ratee = models.ForeignKey('User', related_name='ratings_received')

この集計クエリの例は、を使用して作成された非フィールド値を参照しようとするため、同じように失敗します.extra()

User.ratings_received.extra(
    select={'percent_positive': 'ratings > 3'}
).aggregate(count=Avg('positive'))

1つの回避策

目的の値は、追加の値の定義内で集約データベース関数(この場合はAvg)を使用して直接見つけることができます。

User.ratings.extra(
    select={'percent_positive': 'AVG(rating >= 3)'}
)

このクエリは、次のSQLクエリを生成します。

SELECT (AVG(rating >= 3)) AS `percent_positive`,
       `ratings_rating`.`id`,
       `ratings_rating`.`rating`,
       `ratings_rating`.`rater_id`,
       `ratings_rating`.`ratee_id`
FROM `ratings_rating`
WHERE `ratings_rating`.`ratee_id` = 1

このクエリには不要な列がありますが、値を分離することで、そこから目的の値を取得できpercent_positiveます。

User.ratings.extra(
    select={'percent_positive': 'AVG(rating >= 3)'}
).values('percent_positive')[0]['percent_positive']
于 2011-03-03T17:42:59.980 に答える