Django在模型中制作了另一个字段的字段分组列表

Abh*_*hek 7 django django-models django-orm

我有一个名为MyModel的模型,它有一些虚拟数据,如下所示:

     item    date     value
------------------------------
      ab    8/10/12   124
      ab    7/10/12   433
      ab    6/10/12    99
      abc   8/10/12    23
      abc   7/10/12    80
Run Code Online (Sandbox Code Playgroud)

我想以这样的方式查询这个模型,我得到如下输出:

[{'item': 'ab', 'values': [ 124, 433, 99]},
 {'item': 'abc', 'values': [ 23, 80]}]
Run Code Online (Sandbox Code Playgroud)

我怎么能用django ORM做到这一点?

Tod*_*dor 11

(2016年4月4日)更新:这是Django <= 1.7的工作解决方案.对于较新的版本,请阅读文档中的创建自己的聚合函数.

使用Concat此处获取的自定义聚合(有关该主题的文章)

定义这个:

class Concat(models.Aggregate):
    def add_to_query(self, query, alias, col, source, is_summary):
        #we send source=CharField to prevent Django from casting string to int
        aggregate = SQLConcat(col, source=models.CharField(), is_summary=is_summary, **self.extra)
        query.aggregates[alias] = aggregate

#for mysql
class SQLConcat(models.sql.aggregates.Aggregate):
    sql_function = 'group_concat'

    @property
    def sql_template(self):
        if self.extra.get('separator'):
            return '%(function)s(%(field)s SEPARATOR "%(separator)s")'
        else:
            return '%(function)s(%(field)s)'

#For PostgreSQL >= 9.0
#Aways use with separator, e.g. .annotate(values=Concat('value', separator=','))     
class SQLConcat(models.sql.aggregates.Aggregate):
    sql_function = 'string_agg'

    @property
    def sql_template(self):
        #the ::text cast is a hardcoded hack to work with integer columns
        return "%(function)s(%(field)s::text, '%(separator)s')"

#For PostgreSQL >= 8.4 and < 9.0
#Aways use with separator, e.g. .annotate(values=Concat('value', separator=','))     
class SQLConcat(models.sql.aggregates.Aggregate):
    sql_function = 'array_to_string'

    @property
    def sql_template(self):
        return "%(function)s(array_agg(%(field)s), '%(separator)s')"

#For PostgreSQL < 8.4 you should define array_agg before using it:
#CREATE AGGREGATE array_agg (anyelement)
#(
#    sfunc = array_append,
#    stype = anyarray,
#    initcond = '{}'
#);

class MyModel(models.Model):
    item = models.CharField(max_length = 255)
    date = models.DateTimeField()
    value = models.IntegerField()
Run Code Online (Sandbox Code Playgroud)

所以现在你可以这样做:

>>> from my_app.models import MyModel, Concat
>>> MyModel.objects.values('item').annotate(values=Concat('value'))
[{'item': u'ab', 'values': u'124,433,99'}, {'item': u'abc', 'values': u'23,80'}]
Run Code Online (Sandbox Code Playgroud)

获取values需要手动.split和强制转换为整数的列表int.就像是:

>>> my_list = MyModel.objects.values('item').annotate(values=Concat('value'))
>>> for i in my_list:
...     i['values'] = [int(v) for v in i['values'].split(',')]
...
>>> my_list
[{'item': u'ab', 'values': [124, 433, 99]}, {'item': u'abc', 'values': [23, 80]}]
Run Code Online (Sandbox Code Playgroud)