
Агрегатные функции в Django ORM — крутые. Это обстоятельство послужило поводом добавить еще одну =)
Далее речь пойдет о mysql-специфичной функции
GROUP_CONCAT и Интересующий нас функционал находится в двух модулях,
django.db.models.aggregates и django.db.models.sql.aggregates. Итак, место действия — models.py:from django.db import models ## агрегатные функции ## from django.db.models.aggregates import Aggregate # определение from django.db.models.sql.aggregates import Aggregate as SQLAggregate # реализация
Начнем с самого интересного — собственно реализации.
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)Здесь есть (как минимум) одна некрасивая штука — передача
models.DecimalField() в конструктор родителя; это необходимо для того, чтобы впоследствии результат работы нашей функции не был ошибочно приведен к числовому типу (!). Сломается этот код при очередном обновлении транка Django или нет, мы увидим после очередного обновления транка Django. You have been warned.Реализации готова, перейдем к скучному определению функции:
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Осталось удостовериться, что наше произведение работает:
>>> 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'}]Первый пост на Хабре, уиии!
