Pull to refresh

GROUP_CONCAT для Django ORM

Reading time 2 min
Views 3.3K

Агрегатные функции в Django ORM — крутые. Это обстоятельство послужило поводом добавить еще одну =)

Далее речь пойдет о mysql-специфичной функции GROUP_CONCAT и волшебных розовых пони, как на картинке django-trunk.


Интересующий нас функционал находится в двух модулях, 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'}]


Первый пост на Хабре, уиии!
Tags:
Hubs:
+29
Comments 8
Comments Comments 8

Articles