Django筛选最新transaction_type匹配指定值的学生
筛选最新交易类型匹配指定值的学生
需求概述
需要筛选出满足以下条件的学生及其最新交易记录:
- 每个学生仅保留其**最新(按
transaction_date降序)**的交易记录 - 仅当该最新交易记录的
transaction_type等于指定值时,才保留该记录及对应学生
给定Django模型
class Type(models.Model): type = models.CharField(max_length=50) def __str__(self): return self.type # 注:原代码中return self.durum为笔误,已修正 class Student(models.Model): ogr_no = models.CharField(max_length=10, primary_key=True) ad = models.CharField(max_length=50) soyad = models.CharField(max_length=50) bolum = models.CharField(max_length=100,blank=True) dogum_yeri = models.CharField(max_length=30) sehir = models.CharField(max_length=30) ilce = models.CharField(max_length=30) telefon = models.CharField(max_length=20,blank=True, null=True) nufus_sehir = models.CharField(max_length=30,blank=True, null=True) nufus_ilce = models.CharField(max_length=30,blank=True, null=True) def __str__(self): return f"{self.ogr_no} {self.ad} {self.soyad}" class Transaction(models.Model): student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='islemogrenci') transaction_type = models.ForeignKey(Type, on_delete=models.CASCADE) yardim = models.ForeignKey(Yardim, on_delete=models.CASCADE, blank=True, null=True) aciklama = models.CharField(max_length=300,blank=True, null=True) transaction_date = models.DateTimeField(blank=True,null=True) transaction_user = models.ForeignKey(User, on_delete=models.CASCADE,null=True,blank=True) def __str__(self): return f"{self.student.ad} {self.transaction_type.type}" # 同步修正__str__方法
解决方案
方法1:子查询+聚合函数(兼容所有数据库)
先通过子查询获取每个学生的最新交易日期,再关联筛选出对应记录并匹配交易类型:
from django.db.models import Subquery, OuterRef, Max # 第一步:获取目标交易类型实例 target_type = Type.objects.get(type='busy') # 子查询:计算每个学生的最新交易日期 latest_date_subquery = Transaction.objects.filter( student=OuterRef('pk') ).values('student').annotate( latest_date=Max('transaction_date') ).values('latest_date') # 筛选符合条件的交易记录 filtered_transactions = Transaction.objects.filter( transaction_date=Subquery(latest_date_subquery), transaction_type=target_type ).select_related('student', 'transaction_type') # 提取对应的学生列表 matched_students = [t.student for t in filtered_transactions]
方法2:PostgreSQL窗口函数(性能更优)
利用PostgreSQL支持的ROW_NUMBER()窗口函数,按学生分组并按交易日期倒序排序,直接取每组第一条(最新)记录后筛选类型:
from django.db.models import F, Window from django.db.models.functions import RowNumber target_type = Type.objects.get(type='busy') # 用窗口函数标记每个学生的交易记录顺序,最新记录的row_num为1 ranked_transactions = Transaction.objects.annotate( row_num=Window( expression=RowNumber(), partition_by=F('student'), # 按学生分组 order_by=F('transaction_date').desc() # 按交易日期降序排序 ) ).filter(row_num=1, transaction_type=target_type).select_related('student', 'transaction_type') matched_students = [t.student for t in ranked_transactions]
注意事项
- 确保
transaction_date字段不为空,否则排序逻辑会受影响;若存在空值,可使用Coalesce函数给空值设置一个默认的最早日期:from django.db.models.functions import Coalesce from django.utils.timezone import make_aware(datetime.min) # 在窗口函数或聚合时替换空值 order_by=Coalesce(F('transaction_date'), make_aware(datetime.min)).desc() - 若需要批量处理或优化查询性能,
select_related会预先加载关联的student和transaction_type,避免N+1查询问题。
内容的提问来源于stack exchange,提问作者Ali Kocak
相关产品推荐
相关产品推荐

