Django 1.7和智能,深度,过滤的聚合

时间:2014-08-31 00:39:53

标签: python sql django

我正在使用Django 1.7,我正试图抓住ORM中新功能的优势。

假设我有:

class Player(models.Model):
    name = models.CharField(...)

class Question(models.Model):
    title = models.CharField(...)
    answer1 = models.CharField(...)
    answer2 = models.CharField(...)
    answer3 = models.CharField(...)
    right = models.PositiveSmallIntegerField(...) #choices=1, 2, or 3

class Session(models.Model):
    player = models.ForeignKey(Player, related_name="games")

class RightAnswerManager(models.Manager):

    def get_queryset(self):
        super(RightAnswerManager, self).get_queryset().filter(answer=models.F('question__right'))

class AnsweredQuestion(models.Model):
    session = models.ForeignKey(Session, related_name="questions")
    question models.ForeignKey(Question, ...)
    answer = models.PositiveSmallIntegerField(...) #1, 2, 3, or None if not yet ans.

    objects = models.Manager()
    right = RightAnswerManager()

我知道我能做到:

Session.objects.prefetch_related('questions')

获得有关问题的会议。

我也可以这样做:

Session.objects.prefetch_related(models.Prefetch('questions', queryset=AnsweredQuestion.right.all(), to_attr='answered'))

使用实际回答的问题列表进行会话。

但是我不能对这些进行聚合,以获取-e.g.-元素的数量:

Session.objects.prefetch_related(models.Prefetch('questions', queryset=AnsweredQuestion.right.all(), to_attr='answered')).annotate(total_right=models.Count('answered'))

因为answered不是真实的字段:

FieldError: Cannot resolve keyword 'rightones' into field. Choices are: id, name, sessions

这只是一个示例,因为我的模型中有很多字段我从未包含过。但是这个想法很明确:我无法聚合创建的属性。

有没有办法不落入原始来回答以下问题?

Get each user annotated with their "points".
A user may play any amount of sessions.
In each session it gets many questions to answer.
For each right answer, a point is earned.

在RAW SQL中,它将类似于:

SELECT user.*, COUNT(answeredquestion.id)
FROM user
LEFT OUTER JOIN session ON (session.user_id = user.id)
INNER JOIN answeredquestion ON (answeredquestion.session_id = session.id)
INNER JOIN question ON (answeredquestion.question_id = question.id)
WHERE answeredquestion.answer = question.right
GROUP BY user.id

或类似的东西(因为在分组字段中存在功能依赖性,我将收集用户数据并计算相关的答案项,假设条件通过)。所以RAW查询不适合我。

这个想法是为用户提供总积分。

我可以用两种方式(或两种方式)回答我的问题。

  • 有没有办法在Django 1.7中使用ORM执行相同的查询(实际上我从未测试过这个确切的查询;它在这里提出这个想法),不知何故在相关/反向FK字段上给出了预取或管理器选择? 此处不允许迭代(我有N + 1问题的二次版本!)。
  • 有什么django包以某种方式这样做吗?也许是第三方提供的RAW调用的抽象。 这是因为我会有很多像这样的查询

1 个答案:

答案 0 :(得分:0)

我不相信使用预取会在这种情况下获得任何好处。通常,prefetch_related和select_related用于循环过滤器集并访问每个过滤器集的相关对象。通过在初始查询中进行注释,我相信django会为您进行优化。

对于“让每个用户注释了他们的”点的问题“尝试此查询:

Player.objects.annotate(total_right=models.Count('games__questions__right'))