在Django queryset上注释以逗号分隔的相关项列表

Oli*_*Oli 3 django django-models

从表面上看,这似乎是一个奇怪的要求,但我需要一个逗号分隔的模型相关项目串.从任何教程中获取作者/书籍模型示例,这是我目前正在做的事情:

authors = Authors.objects.all().prefetch_related('books')
for author in authors:
    author.book_titles = ', '.join([book.title for book in author.books.all()])
Run Code Online (Sandbox Code Playgroud)

它并不重,但感觉多余.就像数据库可以做到的那样.在一个理想的世界里,我觉得我应该能够用这些奇特的新数据库功能之一来注释它 .这是一个幻想的例子,使用一个名为的伪造函数Joiner(..):

Authors.objects.annotate(book_titles=Joiner('books__title', separator=', ')
Run Code Online (Sandbox Code Playgroud)

那可能吗?如果是这样,怎么样?

Vla*_*lov 8

from django.db.models import Aggregate, CharField, Value

class GroupConcat(Aggregate):
    function = 'GROUP_CONCAT'
    template = '%(function)s(%(expressions)s)'

    def __init__(self, expression, delimiter, **extra):
        output_field = extra.pop('output_field', CharField())
        delimiter = Value(delimiter)
        super(GroupConcat, self).__init__(
            expression, delimiter, output_field=output_field, **extra)

    def as_postgresql(self, compiler, connection):
        self.function = 'STRING_AGG'
        return super(GroupConcat, self).as_sql(compiler, connection)
Run Code Online (Sandbox Code Playgroud)

用法:

Author.objects.annotate(book_titles=GroupConcat('book__title', ', '))
Run Code Online (Sandbox Code Playgroud)

自定义聚合.这应该适用于SQLite,MySQL和PostgreSQL.

  • 哇哦。这看起来很可怕(而且很棒)。 (4认同)
  • 我做了一些小改动来使这项工作正常进行,但是,是的,现在效果非常好(2s→400ms)。谢谢。 (2认同)
  • 还有一个内置在 `django.contrib.postgres.aggregates` 中的 `StringAgg` 函数现在可以处理这个问题。如果您需要支持多个数据库,上述方法仍然更好。如果您需要多个数据库来使用相同的代码,可以将以下答案作为 `as_mysql` 函数导入。 (2认同)