使用通常的Django模型作为一个例子,我试图用一个作者表来查看,我可以根据它们的书数进行排序。创建书籍计数作为模型方法(如下所示)适用于显示,但这排除了按查询集中的“字段”排序:
class Author(models.Model):
first_name = models.CharField()
last_name = models.CharField()
def book_count(self):
return Book.objects.filter(author=self).count()
class Book(models.Model):
title = models.CharField()
author = models.ForeignKey(Author)
我知道我可以进行post-queryset排序,但我也在应用Django分页,因此检索所有记录可能会有性能损失..
我认为添加Manager Methods就是答案,但我正努力让它发挥作用:
class AuthorManager(models.Manager):
def book_count(self):
return Books.objects.filter(author=self).count()
class Author(models.Model):
title = models.CharField()
author = models.ForeignKey(Author)
objects = AuthorManager()
我的django模板没有返回任何内容。问题是上面的经理的构造,或者如何在模板中调用它? 我假设它只是(这适用于我上面的模型方法,但不适用于管理器方法):
{% for author in authors %}
{{ author.book_count }}
{% endif %}
最终,这就是我想写一个像这样的查询集:
queryset = authors.objects.all().order_by('book_count')
非常感谢!
答案 0 :(得分:2)
那么你的book_count
是一个在模型上定义的函数。但是数据库层对这些函数一无所知:它只知道字段的某些内容,以及Meta
类的一些元素(如unique_together
等)。由于Python通常比SQL具有更强的表达能力,因此Django本身也不会希望 - 在(接近)未来 - 编码诸如SQL查询(或任何其他数据库管理系统)之类的属性。
我们必须做的是将查询转换为聚合,然后对该聚合进行排序。
我们可以使用.annotate
首先使用他们编写的书籍数量注释所有作者,然后对该计数进行排序:
from django.db.models import Count
Author.objects.annotate(book_count=Count('book_set')).order_by('book_count')
这是一个查询集,它会根据从最小数量到最大数量的书籍数量对作者进行排序。我们可以使用'-book_count'
按相反顺序排序。
同样好的一点是你可以访问这个聚合。因此,Author
实例是此查询的结果(不是任何其他查询,必须完成注释),带有book_count
属性,用于存储聚合的结果。
因此,Django将在以下内容中翻译此查询:
SELECT `author`.`id`, `author`.`first_name`, `author`.`last_name`,
COUNT(`book`.`id`) AS `book_count`
FROM `author`
LEFT OUTER JOIN `book` ON (`author`.`id` = `book`.`author_id`)
GROUP BY `author`.`id`
ORDER BY `book_count` ASC
因此,我们使用GROUP BY
执行聚合,以便为所有作者注释他们编写的书籍数量。因此,排序完全在数据库级别完成,这通常比在应用程序级别执行此操作更快。
Manager
我们可以在自定义查询集管理器中注入:
class AuthorManager(models.Manager):
def get_queryset(self):
qs = super(AuthorManager, self).get_queryset()
return qs.annotate(book_count=Count('book_set'))
因此,我们为Author
构建了一个自定义管理器,我们现在可以将其附加到Author
模型中:
class Author(models.Model):
first_name = models.CharField()
last_name = models.CharField()
objects = AuthorManager()
现在我们可以获得根据Author
排序的book_count
个对象:
Author.objects.order_by('book_count')
答案 1 :(得分:2)
您可以将annotate
与自定义QuerySet类一起使用:
from djano.dm.models import Count
class AuthorQuerySet(models.QuerySet):
def annotated(self):
return self.annotate(books_count=Count('book_set'))
class AuthorManager(models.Manager):
def get_queryset(self):
return AuthorQuerySet(self.model, using=self._db)
def annotated(self):
return self.get_queryset().annotated()
class Author(models.Model):
title = models.CharField()
author = models.ForeignKey(Author)
objects = AuthorManager()
现在按书籍排序您可以使用此查询:
Author.object.annotated().order_by('books_count')
要在模板使用中获取带注释的计数:
{{ author.books_count }}