【问题标题】:Django making a list of a field grouping by another field in modelDjango按模型中的另一个字段制作字段分组列表
【发布时间】:2015-05-21 12:40:52
【问题描述】:

我有一个名为 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

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

[{'item': 'ab', 'values': [ 124, 433, 99]},
 {'item': 'abc', 'values': [ 23, 80]}]

我如何使用 django ORM 做到这一点?

【问题讨论】:

  • 您可以获得具有不同查询的项目。然后循环以按项目过滤值。

标签: django django-models django-orm


【解决方案1】:

(2016 年 4 月 4 日)更新:这是适用于 Django Creating your own Aggregate Functions。

使用取自 here 的自定义 Concat 聚合 (an article about the topic)

定义这个:

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()

所以现在你可以这样做了:

>>> 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'}]

要获得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]}]

【讨论】:

  • Concat 不会追加values
  • Concat 是 MySQL group_concat,它会将值连接成字符串。我刚刚测试过了。 django 尝试将该字符串转换为 int 时存在一个小错误,修复方法是在 add_to_query 方法中发送 source=models.CharField()。提供完整代码。
  • postgresql 有类似的吗?
  • 我有 postgresql 9.3,我收到这个错误:KeyError: u'separator'
  • 好的,现在我遇到了一个新的编程错误:..... ProgrammingError: 在 ")" LINE 1 处或附近出现语法错误: ...tring_agg("irmsapp_mymodel"."value", ", ")) 作为“价值观” ...
【解决方案2】:

如果您的 Django 由 PostgreSQL 支持,您可以将 extrastring_agg 结合使用。

https://docs.djangoproject.com/en/1.8/ref/models/querysets/#extra http://www.postgresql.org/docs/9.4/static/sql-expressions.html#SYNTAX-AGGREGATES

假设您发布的表是 ManyToManyRelation,item 实际上是对您的模型 MyModel 的引用,模型表是 mymodel,映射表是 mymodel_value

MyModel.objects.extra(select={
    'values':
        """
        SELECT string_agg(value, ', ' ORDER BY value)
          FROM mymodel_value
         WHERE mymodel.id=mymodel_value.item
        """
}).values('values')

生成的字典将包含一个条目 values,其中一个字符串作为值,该字符串是每个 item 的连接(聚合)值列表。

在 Django shell (./manage.py shell) 中试试这个。您可能需要向子选择添加更多表,以防 ORM 尚未添加这些表。 (主模型表肯定已经存在了。)这取决于模型关系的复杂程度。

打开数据库日志以检查 ORM 生成的查询。

【讨论】:

    猜你喜欢
    • 2021-09-16
    • 2021-10-05
    • 2019-03-11
    • 2014-01-16
    • 2015-09-26
    • 1970-01-01
    • 1970-01-01
    • 2017-01-31
    • 1970-01-01
    相关资源
    最近更新 更多