Django ORM查询,不同的值并加入同一张表

Posted

技术标签:

【中文标题】Django ORM查询,不同的值并加入同一张表【英文标题】:Django ORM query, distinct values and join the same table 【发布时间】:2017-07-13 11:02:52 【问题描述】:

我想根据site 列中的不同值返回所有列,其中hide = 0 并按日期从created 排序。我知道 distinct() 具有指定字段名称的调用目前仅受 PostgresSQL 支持,但我正在运行 mysql。我有一个有效的 SQL 查询(它可能不是很有效),但不知道如何将它转换为 Django ORM。

models.py

from django.db import models
from django.utils import timezone

# Create your models here.

class Results(models.Model):
    user_ip = models.GenericIPAddressField(unpack_ipv4=True)
    site_ip = models.GenericIPAddressField(unpack_ipv4=True)
    site = models.URLField()
    reason = models.CharField(max_length=50)
    hide = models.BooleanField(default=False)
    created = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.site

表结构:

mysql > SHOW CREATE TABLE results\G
*************************** 1. row ***************************
       Table: results
Create Table: CREATE TABLE `results` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_ip` char(39) NOT NULL,
  `site_ip` char(39) NOT NULL,
  `site` varchar(200) NOT NULL,
  `reason` varchar(50) NOT NULL,
  `hide` tinyint(1) NOT NULL,
  `created` datetime(6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=836 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

SQL 查询:

SELECT * FROM 
(
    SELECT site, MAX(created) created 
    FROM results 
    GROUP BY site 
    ORDER BY MAX(created) DESC 
    LIMIT 10
) _d 
JOIN results USING (site, created) 
ORDER BY _d.created DESC

【问题讨论】:

如果你需要 orm 那么你需要发布带有字段的 models.py @Exprator 添加了我的models.py 什么是 hget_scan ??你有这方面的模型吗? 你可以忽略,那是我的测试。我刚刚将表名更新为results,这样就不会那么混乱了。 【参考方案1】:

有一个模块允许您对 Django 模型进行分组,并且仍然可以在结果中使用 QuerySet:https://github.com/kako-nawao/django-group-by

例如:

from django_group_by import GroupByMixin

class ResultsQuerySet(QuerySet, GroupByMixin):
    pass

class Results(Model):
    # your model

class GroupedResultsListView(ListView):
    template_name = 'xxx/results.html'
    model = Results

    def get_queryset(self):
        return Results.objects.group_by('site').annotate(
            max_created=Max('created')).order_by(
            'created').distinct()
        # order by 'max_created' might also work

'xxx/results.html'

<ul>
% for result in object_list %
    <li>
        <h2> result.site </td>
        <p> result.max_created </p>
    </li>
% endfor %
</ul>

annotate/aggregate 基本 Django 查询的区别在于使用相关字段的属性,例如result.site。您还可以在一个查询中按多个属性分组,例如

Results.objects.group_by('site', 'user_ip')

如果需要已经分组的实例的PK,添加如下注解:

.annotate(pks=ArrayAgg('id'))

注意:ArrayAgg 是 Postgres 特定的函数,从 Django 1.9 开始可用:https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/aggregates/#arrayagg

【讨论】:

有没有办法在没有外部模块的情况下做到这一点?【参考方案2】:

我有一个解决方法,但我想知道是否有更好的方法来做到这一点,因为这需要两次数据库命中:

views.py

recent_results_ids = []
[recent_results_ids.append(i.id) for i in Results.objects.raw('SELECT MAX(id) id FROM results WHERE hide = 0 GROUP BY site ORDER BY MAX(created) DESC LIMIT 10')]
recent_results = Results.objects.filter(id__in=recent_results_ids).order_by('-id')

【讨论】:

以上是关于Django ORM查询,不同的值并加入同一张表的主要内容,如果未能解决你的问题,请参考以下文章

从一张表中选择不同的值并限制它们

子查询 django 查询以从对象中获取最大的不同值并返回这些对象

在使用 ORM 的 Django 中,如何对不同的值进行多个自连接

SQL关联两张表查数据,结果只显示一条。

SQL关联两张表查数据,结果只显示一条。

避免多次加入同一张表