Annotate a comma-separated list of related items onto Django queryset

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)

Usage:

Author.objects.annotate(book_titles=GroupConcat('book__title', ', '))

Custom aggregates. This should work on SQLite, MySQL and PostgreSQL.


the accepted answer doesn't pass the separator correctly in MySQL

this lets you set the separator and also specify DISTINCT:

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

    def __init__(self, expression, distinct=False, separator=None, **extra):
        output_field = extra.pop('output_field', CharField())
        distinct = 'DISTINCT ' if distinct else ''
        separator = " SEPARATOR '{}'".format(separator) if separator is not None else ''
        super(GroupConcat, self).__init__(
        expression, separator=separator, distinct=distinct, output_field=output_field, **extra)

see also: GROUP_CONCAT equivalent in Django