从表面上看,这似乎是一个奇怪的事情要求,但我需要一个逗号分隔的模型相关项目串。从任何教程中获取作者/书籍模型示例,这是我目前正在做的事情:
authors = Authors.objects.all().prefetch_related('books')
for author in authors:
author.book_titles = ', '.join([book.title for book in author.books.all()])
它并不重,但感觉多余。就像数据库可以做到的那样。在一个理想的世界里,我觉得我应该能够用这些花哨的新database functions之一来注释这个。这是一个幻想的例子,使用了一个名为Joiner(..)
的虚构函数:
Authors.objects.annotate(book_titles=Joiner('books__title', separator=', ')
这可能吗?如果是这样,怎么样?
答案 0 :(得分:5)
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)
用法:
Author.objects.annotate(book_titles=GroupConcat('book__title', ', '))
Custom aggregates。这应该适用于SQLite,MySQL和PostgreSQL。
答案 1 :(得分:0)
接受的答案没有在MySQL中正确传递分隔符
这允许您设置分隔符并指定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)
答案 2 :(得分:0)
我没有代表发表评论,但我应该补充一点,尽管如果使用MySQL,两个答案都可以使用(只需为接受的答案传递定界符“”即可),group_concat_max_len
默认设置为1024。这意味着您用逗号分隔的字符串将被截断为1024个字符。要增加此值,请使用myql命令:
SET GLOBAL group_concat_max_len = 1000000;
这会将所有数据库中的所有group_concat_max_len
设置为100万个字符,这在大多数情况下就足够了。
答案 3 :(得分:0)
不确定这是否对任何人都有用,但我需要它能够处理不同的值(在聚合列表中仅将每个项目包括一次)。我不知道这是否适用于postgres。
class GroupConcat(Aggregate):
function = 'GROUP_CONCAT'
def __init__(self, expression, delimiter=', ', distinct=False, **extra):
output_field = extra.pop('output_field', CharField())
delimiter = Value(delimiter)
self.template = '%(function)s(DISTINCT %(expressions)s)' if distinct else '%(function)s(%(expressions)s)'
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)