Aggregate functions in Django ORM are cool. This circumstance was the reason to add one more =)

    Next, we will talk about a mysql-specific function GROUP_CONCATand magic pink ponies, as in the django-trunk picture .

    The functional that interests us is in two modules, django.db.models.aggregatesand django.db.models.sql.aggregates. So the scene is

    from django.db import models
    ## aggregate functions ##
    from django.db.models.aggregates import Aggregate # definition
    from django.db.models.sql.aggregates import Aggregate as SQLAggregate # implementation

    Let's start with the most interesting - the actual implementation.

    class SQLConcat (SQLAggregate):
        sql_function = "GROUP_CONCAT"
        def __init __ (self, col, separator = ',', ** extra):
            self.sql_template = "%% (function) s (%% (field) s SEPARATOR '% s')"% separator
            super (SQLConcat, self) .__ init __ (col, source = models.DecimalField (), ** extra)

    There is (at least) one ugly thing - passing models.DecimalField()to the parent constructor; this is necessary so that subsequently the result of the work of our function is not erroneously converted to a numerical type (!). This code will break when you next update the Django trunk or not, we will see after the next update of the Django trunk. You have been warned.

    The implementation is ready, let's move on to the boring definition of a function:

    class Concat (Aggregate):
        name = "Concat"
        def add_to_query (self, query, alias, col, source, is_summary):
            aggregate = SQLConcat (col, is_summary = is_summary, ** self.extra)
            query.aggregates [alias] = aggregate

    It remains to make sure that our work works:

    >>> Post.threads.aggregate (Concat ('id'))
    {'id__concat': '1,2,3,4'}
    >>> from django.db import connection
    >>> connection.queries
    [{'sql': u "SELECT GROUP_CONCAT (` main_post`.`id` SEPARATOR ',') AS `id__concat` FROM` main_post` ",
      'time': '0.000'}]

    The first post on Habré, uiii!

    Also popular now: