Django自定义查询集

时间:2018-05-21 11:12:17

标签: django django-models

使用通常的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')

非常感谢!

2 个答案:

答案 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 }}