【问题标题】:Django: remove duplicates (group by) from queryset by related model fieldDjango:通过相关模型字段从查询集中删除重复项(分组依据)
【发布时间】:2018-05-30 16:16:52
【问题描述】:

我有一个带有几条记录的查询集,我不想使用相关的模型字段删除重复项。例如:

class User(models.Model):
    group = models.ForeignKey('Group')
    ...

class Address(models.Model):
    ...
    models.ForeignKey('User')

addresses = Address.objects.filter(user__group__id=1).order_by('-id')

这会返回一个地址记录的 QuerySet,我想按用户 ID 分组。

  • 我不能使用.annotate,因为我需要地址中的所有字段,以及地址和用户之间的关系
  • 我不能使用.distinct(),因为它不起作用,因为所有地址都是不同的,我想要不同的用户地址。

我可以:

addresses = Address.objects.filter(user__group__id=1).order_by('-id')
unique_users_ids = []
unique_addresses = []

for address in addresses:
    if address.user.id not in unique_users_ids:
        unique_addresses.append(address)
        unique_users_ids.append(address.user.id)

print unique_addresses # TA-DA!

但是对于像 group by(该死的 Django)这样简单的事情来说似乎太过分了。

有没有简单的方法来实现这一点?

【问题讨论】:

  • AddressGroup 有什么关系?我们只看到AddressUser相关,而User与分组相关,那么查询不会是user__group__id=1吗?
  • 完全不相关。
  • 此外,我们无法执行GROUP BY,因为目前还不清楚您将如何“聚合”多个地址。
  • 但是group__id=1 查询没有多大意义。
  • 最新:.order_by('-id')

标签: python django


【解决方案1】:

通过使用带有字段名称的.distinct()

Django 还有一个.distinct(..) 函数,它将应该唯一的列名作为输入列。唉,大多数数据库系统不支持这一点(据我所知,只有 PostgreSQL)。但是在 PostgreSQL 中我们可以这样执行:

# Limited number of database systems support this
addresses = (Address.objects
                    .filter(user__group__id=1)
                    .order_by('-id')
                    .distinct('user_id'))

通过使用两个查询

另一种处理方法是首先对用户进行查询,然后为每个用户获取最大的address_id

from django.db.models import Max

address_ids = (User.objects
                   .annotate(address_id=Max('address_set__id'))
                   .filter(address_id__isnull=False)
                   .values_list('address_id'))

所以现在对于每个用户,我们计算了最大对应的address_id,并且我们排除了没有地址的Users。然后我们得到ids的列表。

在第二步中,我们然后获取地址:

addresses = Address.objects.filter(<b>pk__in=address_ids</b>)

【讨论】:

  • 感谢这个人。我有一个非常具体的查询,使用filter()extra()order_by(),我需要对所有数据进行重复数据删除(~1M 记录),有一个特定的要求(我必须对这个大型查询集进行重复数据删除,但还要跟踪已删除的内容等)。也许如果我有 Django ORM 的博士学位,我就可以完成它,但唯一现实的事情是用原始 SQL 重写它。但最后,只需进行两个查询,其中一个作为过滤器引用,就可以了。
猜你喜欢
  • 2019-05-24
  • 2018-10-26
  • 2019-06-23
  • 2015-08-31
  • 1970-01-01
  • 2014-01-18
  • 1970-01-01
  • 2020-07-31
  • 1970-01-01
相关资源
最近更新 更多