Django - 按列分组以及在不同列上的位置
Django - Group by column and where on a different column
这些是我的 django 模型(简化):
class Status(models.Model):
status = models.CharField(max_length=20)
class Project(models.Model):
client = models.ForiegnKey(Client)
class TicketRequest(models.Model):
status = models.ForiegnKey(Status, related_name='ticket_requests')
project = models.ForiegnKey(Project)
created = models.DateTimeField()
要求的结果
status.value | client.id | count
--------------+-------------+---------
is_assigned | 4 | 2
is_closed | 4 | 66
is_open | 4 | 7
查询集注释
Status.objects.filter(
ticket_requests__project__client_id=4
).values('value').annotate(
count=Count('ticket_requests__project__client')
)
returns
[{'count': 2, 'value': u'is_assigned'}, {'count': 66, 'value': u'is_closed'}, {'count': 7, 'value': u'is_open'}]
这正是所需要的。
但我需要使用今天、本周和月份等过滤器在 TicketRequest.created
上过滤查询集。
由于这些过滤器需要大量重复使用,我创建了一个方便的助手:
def qs_time_range(qs, time_range, field_name):
now = timezone.now()
if time_range == 'month':
past = now - timedelta(days=30)
return qs.filter(
**{field_name + '__date__range': [past, now]}
)
问题
当我使用助手过滤工单请求时,结果不是我所期望的。
data = filters.qs_time_range(
Status.objects.filter(ticket_requests__project__client_id=4),
'month',
'ticket_requests__created'
).values('value').annotate(
count=Count('ticket_requests__project__client')
)
结果
[{'count': 27, 'value': u'is_assigned'}]
结果应该是这样:
[{'count': 3, 'value': u'is_assigned'}, {'count': 3, 'value': u'is_closed'}, {'count': 3, 'value': u'is_open'}]
问题:过滤是否弄乱了 SQL?我应该尝试什么?
附加信息 - 原始 SQL
data.query
揭示了这个 SQL(为 Postgres 修改)
SELECT "tickets_status"."value",
COUNT("projects_project"."client_id") AS "count"
FROM "tickets_status"
INNER JOIN "tickets_ticketrequest" ON ("tickets_status"."id" = "tickets_ticketrequest"."status_id")
INNER JOIN "projects_project" ON ("tickets_ticketrequest"."project_id" = "projects_project"."id")
INNER JOIN "tickets_ticketrequest" T5 ON ("tickets_status"."id" = T5."status_id")
WHERE ("projects_project"."client_id" = 4
AND T5."created"::date BETWEEN '2016-09-02' AND '2016-10-02')
GROUP BY "tickets_status"."value",
"tickets_status"."name"
ORDER BY "tickets_status"."name" ASC;
哪个returns
value | count
-------------+-------
is_assigned | 27
(1 row)
但这是我想要生成的 SQL:
SELECT "tickets_status"."value",
COUNT("projects_project"."client_id") AS "count"
FROM "tickets_status"
INNER JOIN "tickets_ticketrequest" ON ("tickets_status"."id" = "tickets_ticketrequest"."status_id")
INNER JOIN "projects_project" ON ("tickets_ticketrequest"."project_id" = "projects_project"."id")
WHERE ("projects_project"."client_id" = 4
AND "tickets_ticketrequest"."created"::date BETWEEN '2016-09-02' AND '2016-10-02')
GROUP BY "tickets_status"."value",
"tickets_status"."name"
ORDER BY "tickets_status"."name" ASC;
哪个returns 正确的结果
value | count
-------------+-------
is_assigned | 3
(3 rows)
很高兴听到你的东西开始工作了,但这个答案是这个魔法背后的推理的 1 个解释。我记得这行得通,但如果有人想出为什么行得通的解释,那才是真正正确的答案。但是,是的,如果 django.db.models.Count
没有按预期工作,您可以将其替换为 Sum(Case(When(field='value'), then=1), default=0, output_field=models.IntegerField())
(在 Django >= 1.9 中)的组合。
原来解决问题的评论:
First of all have to state the obvious: are you sure you have any
closed or open ticket requests created in the last month? That would
be the most obvious cause. But if you already double- and
triple-checked that, then I have a vague memory of having a similar
problem somewhere, but can't remember why. But I fixed it by changing
Count() to Sum(Case(When(set__the__condition='set_the_value'),
then=1), default=0, output_field=models.IntegerField())).
这些是我的 django 模型(简化):
class Status(models.Model):
status = models.CharField(max_length=20)
class Project(models.Model):
client = models.ForiegnKey(Client)
class TicketRequest(models.Model):
status = models.ForiegnKey(Status, related_name='ticket_requests')
project = models.ForiegnKey(Project)
created = models.DateTimeField()
要求的结果
status.value | client.id | count
--------------+-------------+---------
is_assigned | 4 | 2
is_closed | 4 | 66
is_open | 4 | 7
查询集注释
Status.objects.filter(
ticket_requests__project__client_id=4
).values('value').annotate(
count=Count('ticket_requests__project__client')
)
returns
[{'count': 2, 'value': u'is_assigned'}, {'count': 66, 'value': u'is_closed'}, {'count': 7, 'value': u'is_open'}]
这正是所需要的。
但我需要使用今天、本周和月份等过滤器在 TicketRequest.created
上过滤查询集。
由于这些过滤器需要大量重复使用,我创建了一个方便的助手:
def qs_time_range(qs, time_range, field_name):
now = timezone.now()
if time_range == 'month':
past = now - timedelta(days=30)
return qs.filter(
**{field_name + '__date__range': [past, now]}
)
问题
当我使用助手过滤工单请求时,结果不是我所期望的。
data = filters.qs_time_range(
Status.objects.filter(ticket_requests__project__client_id=4),
'month',
'ticket_requests__created'
).values('value').annotate(
count=Count('ticket_requests__project__client')
)
结果
[{'count': 27, 'value': u'is_assigned'}]
结果应该是这样:
[{'count': 3, 'value': u'is_assigned'}, {'count': 3, 'value': u'is_closed'}, {'count': 3, 'value': u'is_open'}]
问题:过滤是否弄乱了 SQL?我应该尝试什么?
附加信息 - 原始 SQL
data.query
揭示了这个 SQL(为 Postgres 修改)
SELECT "tickets_status"."value",
COUNT("projects_project"."client_id") AS "count"
FROM "tickets_status"
INNER JOIN "tickets_ticketrequest" ON ("tickets_status"."id" = "tickets_ticketrequest"."status_id")
INNER JOIN "projects_project" ON ("tickets_ticketrequest"."project_id" = "projects_project"."id")
INNER JOIN "tickets_ticketrequest" T5 ON ("tickets_status"."id" = T5."status_id")
WHERE ("projects_project"."client_id" = 4
AND T5."created"::date BETWEEN '2016-09-02' AND '2016-10-02')
GROUP BY "tickets_status"."value",
"tickets_status"."name"
ORDER BY "tickets_status"."name" ASC;
哪个returns
value | count
-------------+-------
is_assigned | 27
(1 row)
但这是我想要生成的 SQL:
SELECT "tickets_status"."value",
COUNT("projects_project"."client_id") AS "count"
FROM "tickets_status"
INNER JOIN "tickets_ticketrequest" ON ("tickets_status"."id" = "tickets_ticketrequest"."status_id")
INNER JOIN "projects_project" ON ("tickets_ticketrequest"."project_id" = "projects_project"."id")
WHERE ("projects_project"."client_id" = 4
AND "tickets_ticketrequest"."created"::date BETWEEN '2016-09-02' AND '2016-10-02')
GROUP BY "tickets_status"."value",
"tickets_status"."name"
ORDER BY "tickets_status"."name" ASC;
哪个returns 正确的结果
value | count
-------------+-------
is_assigned | 3
(3 rows)
很高兴听到你的东西开始工作了,但这个答案是这个魔法背后的推理的 1 个解释。我记得这行得通,但如果有人想出为什么行得通的解释,那才是真正正确的答案。但是,是的,如果 django.db.models.Count
没有按预期工作,您可以将其替换为 Sum(Case(When(field='value'), then=1), default=0, output_field=models.IntegerField())
(在 Django >= 1.9 中)的组合。
原来解决问题的评论:
First of all have to state the obvious: are you sure you have any closed or open ticket requests created in the last month? That would be the most obvious cause. But if you already double- and triple-checked that, then I have a vague memory of having a similar problem somewhere, but can't remember why. But I fixed it by changing Count() to Sum(Case(When(set__the__condition='set_the_value'), then=1), default=0, output_field=models.IntegerField())).