复杂的 Django ORM 注释和聚合

Posted

技术标签:

【中文标题】复杂的 Django ORM 注释和聚合【英文标题】:Complex Django ORM Annotations & Aggregations 【发布时间】:2021-01-20 09:57:32 【问题描述】:

我目前正在将一些记录的项目准备为 JSON 序列化格式。

我正在尝试通过 Django 的内置 ORM,利用注释和聚合来做到这一点。

我正在尝试为每个“记录的项目”复制以下结构:

...

    "count": 20,
    "metric-type": "total-dataset-requests",
    "access-method": "regular",
    "country-counts": 
        "au": 6,
        "jp": 1,
        "us": 13
    

...

我目前根据自己的知识构建了这个queryset

metrics = LoggedItem.objects.filter(
    identifier=record['identifier'],
    hit_type='investigations',
    is_machine=False,
    is_robot=False
).values(
    'identifier', 'country'
).annotate(
    count=models.Count('identifier'),
    metric_type=models.Value("total-dataset-requests", output_field=models.CharField()),
    access_method=models.Value("regular", output_field=models.CharField())
)

这给了我一个<Queryset []>,如下:

<QuerySet ['identifier': '10.80519/954e-4db4', 'country': 'fr', 'count': 1, 'metric_type': 'total-dataset-requests', 'access_method': 'regular', 'identifier': '10.80519/954e-4db4', 'country': 'gb', 'count': 5, 'metric_type': 'total-dataset-requests', 'access_method': 'regular']>

如您所见,我拥有模仿上述数据结构所需的所有数据。但是以一种看起来晦涩难懂的格式......我可以使用 Python 从 values_list() 或 iterator() 中进行清理,但我想通过 ORM 在数据库层上完成大部分繁重的工作。

所以我想我的问题是,我怎样才能复制 JSON 结构中显示的聚合子作为 ORM 查询...?

仅供参考:了解来自 models.py 的完整 LogItem 可能会很有用:

class LogItem(models.Model):
    ....

    id = models.AutoField(_('ID'), primary_key=True)

    session_id = models.TextField(_('Session ID'), default='')

    doubleclick_id = models.TextField(_('Doubleclick ID'), default='')

    event = models.DateTimeField(_('Event Recorded At'), default=now, blank=True)

    client_ip = models.CharField(_('Client IP'), max_length=15, null=True, blank=True)

    session_cookie_id = models.CharField(_('Session Cookie ID'), max_length=120, null=True, blank=True)

    user_cookie_id = models.CharField(_('User Cookie ID'), max_length=120, null=True, blank=True)

    user_id = models.CharField(_('User ID'), max_length=100, null=True, blank=True)

    request_url = models.TextField(_('Request URL'), default='')

    identifier = models.CharField(_('Identifier'), max_length=120, null=True, blank=True)

    filename = models.TextField(_('Filename'), null=True)

    size = models.PositiveBigIntegerField(_('Size'), null=True)

    user_agent = models.TextField(_('User Agent'), default='')

    # Alpha-2 ISO 3166 Codes: [Reference: Country Codes Alpha-2 & Alpha-3](https://www.iban.com/country-codes)
    country = models.CharField(_('Country'), max_length=10, default='gb')

    hit_type = models.CharField(_('Hit Type'), max_length=60, null=True, blank=True)

    is_robot = models.BooleanField(_('Is Robot'), default=False)

    is_machine = models.BooleanField(_('Is Machine'), default=False)

【问题讨论】:

你应该把你拥有的结构映射到你想要的代码结构中。 @bryan60 不幸的是,我需要这种格式的日志来进行另一个验证步骤。 所以在这一步之前映射它? 他们一次记录一个。 您并不清楚您需要什么或您的意思。从我在您的问题中看到的,您已经在数据库层完成了所有繁重的查询并获得了您想要的聚合信息,只是格式略有不同。如果这是真的,那么这里的解决方案显然只是使用您的查询集并对其进行修改,而不是试图哄骗 SQL orm 为您提供非表格数据。如果没有,您应该澄清您在需要的结果中没有得到什么,除了格式化。取数据是数据库的事情,格式化是为了视图。 【参考方案1】:

我认为您只能在 python 而不是 QuerySet 中执行此操作,因为 Queryset 是数据库“SELECT”表示,据我所知,您不能在查询集中包含字典“country-counts”而不是构建结果在 custom model Serializer Method 或 python 代码中:

序列化程序示例

    class LogItemSerializer(serializers.ModelSerializer):
        country_counts = serializers.SerializerMethodField()

        class Meta:
            model = LogItem

        def get_country_counts(self, obj):
            # create the dic you want
            result = 
                  "au": 6,
                  "jp": 1,
                  "us": 13
                  
            return result

【讨论】:

以上是关于复杂的 Django ORM 注释和聚合的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在 Django Querysets 中组合注释和过滤聚合?

072:Django数据库ORM聚合函数详解-aggregate和annotate

使用SQL语言了解Django ORM中的分组(group by)和聚合(aggregation)查询

Django中的聚合/分组查询/F/Q查询/orm执行原生sql语句/ ORM事务和锁

070:Django数据库ORM聚合函数详解-准备工作

Django(10)ORM聚合查询