Django ORM: Агрегатор текста на подзапросе
Я бился головой над этим:
У меня есть 2 модели, и я пытаюсь разработать пользовательский менеджер/кейрисет, который позволит мне аннотировать к каждой Series
id
из связанных пазлов, удовлетворяющих определенным условиям в формате '2,13,26'
.
Упрощенные модели:
class Series(models.Model):
puzzles = models.ManyToManyField(
Puzzle, through='SeriesElement', related_name='series')
is_public = models.BooleanField(null=False, blank=False, default=False)
class Puzzle(models.Model):
pass
мой пользовательский агрегатор:
from django.db.models.aggregates import Aggregate
from django.db.models.functions import Coalesce
from django.db.models.fields import CharField
from django.db.models.expressions import Value
class GroupConcat(Aggregate):
"""
according to https://stackoverflow.com/questions/10340684/group-concat-equivalent-in-django
according to https://stackoverflow.com/a/55216659
would be compatible with MySQL and SQLite
"""
function = 'GROUP_CONCAT'
def __init__(self, expression, distinct=False, ordering=None, **extra):
super(GroupConcat, self).__init__(expression,
distinct='DISTINCT ' if distinct else '',
ordering=' ORDER BY %s' % ordering if ordering is not None else '',
output_field=CharField(),
**extra)
def as_sqlite(self, compiler, connection, **extra):
return super().as_sql(compiler,
connection,
template='%(function)s(%(distinct)s%(expressions)s%(ordering)s)',
**extra)
один ориентир для достижения моей цели:
pzl_sub = apps.get_model('puzzles', 'Puzzle').objects.filter(series__id= OuterRef('id'))
pzl_sub = pzl_sub.filter(series_elements__isnull=False).add_nb_public_series().filter(nb_public_series=5)
pzl_ids= pzl_sub.order_by().values('id')
qs = Series.objects.annotate(id_str_pzl = GroupConcat(pzl_ids))
Я получаю только один puzzle.id, который соответствует заданным условиям, вместо конкатената всех puzzle.id, которые соответствуют условиям
Есть подсказка, что я делаю неправильно?
После нескольких дней борьбы и тестирования всего и вся, я наконец-то разобрался:
pzl_sub = apps.get_model('puzzles', 'Puzzle').objects.filter(series__id= OuterRef('id'))
pzl_sub = pzl_sub.filter(series_elements__isnull=False).add_nb_public_series().filter(nb_public_series=5)
pzl_ids= pzl_sub.order_by().values('series__id')
qs = Series.objects.annotate(id_str_pzl = Subquery(pzl_ids.annotate(result=GroupConcat('id')).order_by().values('result')))
в pzl_ids
, нужно выделить values('series__id')
и только на втором шаге, нам нужно аннотировать агрегатором, и снова выделить полученное значение ...