django通过关联表字段进行排序并去重

在 Django 中,要通过关联模型(Pathogen 表)的 pathogen_name 字段对 Sample 表的 queryset 进行排序,可以使用 Django ORM 的 annotate 和 order_by 方法。

以下是具体的实现方式:

假设的模型结构

python 复制代码
class Sample(models.Model):
    name = models.CharField(max_length=100)

class Pathogen(models.Model):
    pathogen_name = models.CharField(max_length=100)
    sample = models.ForeignKey(
        Sample, 
        on_delete=models.SET_NULL, 
        verbose_name='样品编号', 
        null=True, 
        related_name='virus'
    )

问题?

Sample.objects.order_by('virus__pathogen_name') 返回的结果有重复的sample_id,如何去掉他们?

在 Django ORM 中,order_by('virus__pathogen_name') 会导致重复的 Sample 对象出现,因为它基于关联表的多对一关系。如果你想去掉重复的 Sample 对象,可以使用 .distinct() 方法。不过,distinct() 的行为依赖于数据库后端。

以下是解决重复 sample_id 的方法:

方法 1:使用 .distinct() 去重

python 复制代码
Sample.objects.order_by('virus__pathogen_name').distinct()
  • 解释 :distinct() 会去掉重复的 Sample 实例(基于主键)。此方法在大多数情况下有效,但如果排序字段涉及多表 JOIN,一些数据库(例如 MySQL)可能不支持完全去重,尤其是当使用 order_by 时。

方法 2:通过 annotate 获取第一个关联的 Pathogen 并排序

如果 .distinct() 无法正常去重,你可以通过 annotate 方式解决,只取与 Sample 关联的第一个 Pathogen.pathogen_name。

python 复制代码
from django.db.models import Min

Sample.objects.annotate(
    first_pathogen_name=Min('virus__pathogen_name')
).order_by('first_pathogen_name')
  • 解释 :
    • Min('virus__pathogen_name') 获取与每个 Sample 关联的最小(按字母排序第一个)pathogen_name。
    • 这样每个 Sample 只会出现一次,且按照 first_pathogen_name 排序。

方法 3:使用子查询避免重复

使用 Subquery 和 distinct 结合可以更灵活地解决问题:

python 复制代码
from django.db.models import Subquery, OuterRef

Sample.objects.filter(
    pk__in=Subquery(
        Pathogen.objects.filter(
            sample=OuterRef('pk')
        ).order_by('pathogen_name').values('sample').distinct()
    )
).order_by('virus__pathogen_name')
  • 解释 :
    • 使用 Subquery 获取去重后的 Sample 主键。
    • 确保最终只返回唯一的 Sample 对象。

总结

  • 如果 .distinct() 能够满足需求,直接使用是最简单的方式。
  • 如果需要更精确的控制,annotate 和 Subquery 提供更灵活的解决方案。推荐结合你的排序逻辑和数据库类型测试最佳方法。
相关推荐
小码哥_常1 小时前
一套自己部署的 IM,客户端是同一份 Flutter
前端·后端
Decay__1 小时前
05_MySQL 常用 SQL 大全 —— 诊断、管理、优化辅助 SQL 与三大系统库速查
运维·数据库·sql·mysql·架构
学长毕设4 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
小徐不徐说6 小时前
C++ 接口与插件设计:从工厂注册到 Qt 动态插件
数据库·c++·qt·系统架构·程序设计
努力的小雨6 小时前
给 AI 眼镜开发应用有多难?我从零做了一个陪练,还上架了
后端
论文复现现场6 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
Decay__7 小时前
04_MySQL DBA 日常运维手册 —— 会话管理、锁与阻塞、事务监控、慢查询与日志巡检
运维·数据库·sql·mysql·架构
excel8 小时前
双 Token 与前端使用 Crypto 公私钥鉴权换 Token,哪种方案更好?
前端·后端
AI入职记8 小时前
AI 代码审查召回率实测:20 个骗过单元测试的变异体,抓到 13 个
后端
浮链序8 小时前
别让 AI 客服把用户当猴耍:百来行 Python 把「转人工」做成一条高可用链路
后端