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
相关产品推荐
相关产品推荐

