You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中distinct后排序报错的解决方案及替代实现

问题

我有一个自定义QuerySet方法,用于筛选拥有至少一笔抵押贷款的用户。为避免重复数据,我添加了.distinct('pk')。但问题在于API支持按需排序,这意味着我无法在调用distinct前预先排序。当前尝试排序时会抛出如下错误:

ProgrammingError
SELECT DISTINCT ON expressions must match initial ORDER BY expressions
LINE 1: SELECT COUNT(*) FROM (SELECT DISTINCT ON ("users_user"."id")...

关联模型定义如下:

applicant = models.ForeignKey('User', verbose_name='Žiadateľ', on_delete=models.PROTECT,
                              related_name='mortgages')

自定义QuerySet代码:

class UserQuerySet(models.QuerySet):
    def is_applicant(self):
        return self.filter(mortgages__isnull=False).distinct('pk')

请问如何解决排序报错问题?能否不使用distinct实现is_applicant方法?

解决方案

一、解决排序与distinct('pk')的冲突问题

PostgreSQL的DISTINCT ON语法要求排序的起始字段必须和DISTINCT ON指定的字段完全匹配,这是报错的核心原因。要兼容按需排序,可按以下方式处理:

  • 调用排序方法时,先将DISTINCT ON指定的字段(即用户的pk)放到排序字段的最前面,再追加其他排序规则。比如API需要按create_time倒序排序:
    users = User.objects.is_applicant().order_by('pk', '-create_time')
    
  • 若在DRF视图中处理动态排序,可重写get_queryset方法,自动把pk插入排序字段首位:
    from rest_framework.views import APIView
    
    class UserListView(APIView):
        def get_queryset(self):
            queryset = User.objects.is_applicant()
            sort_by = self.request.query_params.get('sort', 'pk')
            order_fields = ['pk'] + sort_by.split(',')
            return queryset.order_by(*order_fields)
    

二、不使用distinct实现is_applicant方法

完全可以不用distinct,以下两种方式更高效且能避免重复数据:

1. 使用Exists()子查询

通过子查询判断用户是否存在关联的抵押贷款,无需处理JOIN带来的重复:

class UserQuerySet(models.QuerySet):
    def is_applicant(self):
        return self.filter(
            models.Exists(Mortgage.objects.filter(applicant=models.OuterRef('pk')))
        )

2. 使用__in关联查询

先获取所有有抵押贷款的用户ID集合,再用该集合筛选用户表:

class UserQuerySet(models.QuerySet):
    def is_applicant(self):
        applicant_ids = Mortgage.objects.values_list('applicant_id', flat=True).distinct()
        return self.filter(pk__in=applicant_ids)

内容的提问来源于stack exchange,提问作者Milano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 01:06:40