和的Django等价物+;案例
我有一堆django模特儿和的Django等价物+;案例,django,django-models,Django,Django Models,我有一堆django模特儿 class ReviewItem(Model): review = models.ForegnKey("Review") person = models.ForeignKey("Person") category = models.ForeignKey("Category") item = models.ForeignKey("item") reviewed = models.DateTimeField(null=True) class Pers
class ReviewItem(Model):
review = models.ForegnKey("Review")
person = models.ForeignKey("Person")
category = models.ForeignKey("Category")
item = models.ForeignKey("item")
reviewed = models.DateTimeField(null=True)
class Person(Model):
name = models.CharField(max_length=255)
class Category(Model):
name = models.CharField(max_length=127)
class Item(Model):
name = models.CharField(max_length=127)
category = models.ForeignKey("Category")
(如您所见,ReviewItem中的“类别”fk是多余的)
最多会有NxM ReviewItem记录,其中N是人数,M是他们可能分配给他们的项目数量,他们将在审查后设置“审查”日期。项目分为多个类别
我想要的是计算每个项目中有多少项目已经审核,有多少没有审核。在SQL中,我可以
select category.name, item.name,
sum(case when reviewed is null then 1 else 0 end) as un_reviewed
sum(case when reviewed is null then 0 else 1 end) as reviewed
from reviewitem
join category on category.id = reviewitem.category_id
join item on item.id = reviewitem.item_id
group by category.id, item.id
order by category.name, item.name
如果不在django中执行两个单独的查询集,我就不知道如何执行此操作
通过两个查询集,我得到了:
uncompleted_items = Item.objects.filter(
reviewitem__review=current_review,
reviewitem__person__reports_to=eff_user,
reviewitem__reviewed__isnull=True
).select_related(
'category',
).annotate(num_uncompleted=Count('reviewitem'))
completed_items = Item.objects.filter(
reviewitem__review=current_review,
reviewitem__person__reports_to=eff_user,
reviewitem__reviewed__isnull=False
).select_related(
'category',
).annotate(num_completed=Count('reviewitem'))
尽管这样做不太方便,但使用Django ORM并不是不可能做到这一点:) 好吧,黑客解决方案不起作用,所以这里有一个漂亮的解决方案;) 生成的查询:
SELECT "test_models_category"."name",
"test_models_item"."name",
COUNT("test_models_reviewitem"."reviewed" IS NULL) AS "unreviewed",
COUNT("test_models_reviewitem"."reviewed" IS NOT NULL) AS "reviewed"
FROM "test_models_reviewitem"
INNER JOIN "test_models_item" ON ("test_models_reviewitem"."item_id" = "test_models_item"."id")
INNER JOIN "test_models_category" ON ("test_models_item"."category_id" = "test_models_category"."id")
GROUP BY "test_models_category"."name",
"test_models_item"."name"
示例结果:
[{'item__category__name': u'cat a',
'item__name': u'aa',
'reviewed': 1,
'unreviewed': 1},
{'item__category__name': u'cat a',
'item__name': u'ab',
'reviewed': 1,
'unreviewed': 1},
{'item__category__name': u'cat b',
'item__name': u'ba',
'reviewed': 1,
'unreviewed': 1},
{'item__category__name': u'cat b',
'item__name': u'bb',
'reviewed': 1,
'unreviewed': 1}]
Paul Tomblin的更新
如上所述的CountNull和CountNotNull方法不起作用(很明显,布尔值无论是真是假都计为1),因此我对它们进行了如下更改:
from django.db import models
class CountNullSql(models.sql.aggregates.Sum):
sql_template = '%(function)s((%(field)s IS NULL)::integer)'
class CountNotNullSql(CountNullSql):
sql_template = '%(function)s((%(field)s IS NOT NULL)::integer)'
class CountNull(models.Sum):
sql = CountNullSql
def add_to_query(self, query, alias, col, source, is_summary):
aggregate = self.sql(
col,
source=source,
is_summary=is_summary,
**self.extra)
query.aggregates[alias] = aggregate
def _default_alias(self):
return '%s__%s' % (self.lookup, self.sql.__class__.__name__.lower())
default_alias = property(_default_alias)
class CountNotNull(CountNull):
sql = CountNotNullSql
试试这个:
params = {'ri_table': ReviewItem._meta.db_table, 'i_table': Item._meta.db_table}
reviewed_query = 'SELECT COUNT(*) FROM %(ri_table)s WHERE %(ri_table)s.item_id=%(i_table)s.id AND reviewed IS NOT NULL' % params
unreviewed_query = 'SELECT COUNT(*) FROM %(ri_table)s WHERE %(ri_table)s.item_id=%(i_table)s.id AND reviewed IS NULL' % params
items = Item.objects.extra(select={'reviewed': reviewed_query, 'unreviewed': unreviewed_query})
这将返回一个项的查询集,这些项本质上用审阅和未审阅的计数进行了注释。每个项目都是一个模型对象,因此可以从中获取类别等print items.query
将向您显示SQL语句,但当然,Django magic会进一步构建模型实例对象
在一个查询中收集所有这些数据对于Django ORM来说并不优雅(公平地说,它也是一种非平凡的SQL)。这已经够复杂了,我认为不值得去做,至少一开始不值得。Django ORM范例中更简单的代码将更容易理解、更快地编写代码并更易于维护。它可能(也可能不)需要对数据库进行更多的点击,但这种担心可能是过早的优化。模式规范化之类的事情可能会使您获得更大的性能收益。根据您的使用情况,像这样获取计数实际上可能会更快
reviewed = item_obj.reviewitem_set.exclude(reviewed__isnull=True).count()
unreviewed = item_obj.reviewitem_set.filter(reviewed__isnull=True).count()
这可以通过Django 1.8中的内置支持直接完成。请参阅。对于Django 1.8,可以使用 因此,您的示例查询可能如下所示:
from django.db.models import When, Case, Sum, IntegerField
items = Item.objects.annotate(
un_reviewed=Sum(Case(When(reviewed__isnull=True, then=1)
When(reviewed__isnull=False, then=0),
output_field=IntegerField())),
reviewed=Sum(Case(When(reviewed__isnull=True, then=0)
When(reviewed__isnull=False, then=1),
output_field=IntegerField())))
很抱歉“maxLength”-我实际上是从内存中输入的。不用担心:)这个答案大概是你想要的还是你需要其他的?有多种方法可以做到这一点,但我认为这是最简单的方法。我会在早上试一试,然后让你知道。嗯。。。回到绘图板上,我想,我会给你回复:)这是返回对象,还是一个dict?澄清一下。您是否在查找每个类别的已审核和未审核项目的数量?或者每个项目的审查次数?您是只想要计数还是想要模型对象的查询集?我想要为每个
项目设置了已审核日期的已审核项目的数量,以及没有为每个项目设置了已审核日期的数量,但我还想要项目和类别模型对象(或每个字段中至少有两个字段)。你有机会尝试一下吗?让我们知道什么是有效的。我严重怀疑使用“选择计数”子查询是否会是最优的。我同意选择计数可能不会赢得任何速度竞赛,但这是一个解决方案,或者至少是一个起点。你对帮助你的人做出了尖刻的回应……我想知道为什么。这不是尖刻的回应,而是一个原因为什么我认为在你写答案之前已经给出的答案更好。好吧,这很公平。得到最好、最合适的答案就是目标。干杯。
from django.db.models import When, Case, Sum, IntegerField
items = Item.objects.annotate(
un_reviewed=Sum(Case(When(reviewed__isnull=True, then=1)
When(reviewed__isnull=False, then=0),
output_field=IntegerField())),
reviewed=Sum(Case(When(reviewed__isnull=True, then=0)
When(reviewed__isnull=False, then=1),
output_field=IntegerField())))