如何使用Django ORM编写查询,获取每个Ticket关联的最新TicketDetails记录及全量字段
解决Django中关联获取最新关联记录的问题
嘿,我来帮你搞定这个Django查询的问题!你需要获取Ticket表的所有字段,同时关联每个Ticket对应的最新一条TicketDetails记录,对吧?下面我给你两种靠谱的实现方式,都是基于Django ORM的,也可以用原生SQL兜底。
首先先确认下你的Django模型结构(根据你给出的表结构转换):
from django.db import models class Ticket(models.Model): name = models.CharField(max_length=100) type = models.CharField(max_length=100) user = models.CharField(max_length=100) class TicketDetails(models.Model): ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, related_name='details') message = models.TextField() created_time = models.DateTimeField()
方法一:用Subquery和OuterRef(推荐的ORM方式)
这种方式完全用Django的ORM实现,不需要写原生SQL,兼容性更好。
首先导入需要的模块:
from django.db.models import OuterRef, Subquery, Max
然后分三步构建查询:
- 先找到每个
Ticket对应的最新created_time - 根据这个时间找到对应的
TicketDetails记录 - 把这些字段关联到
Ticket的查询结果中
完整代码:
# 子查询1:获取每个ticket对应的最新消息时间 latest_created_time = TicketDetails.objects.filter( ticket=OuterRef('pk') ).values('ticket').annotate( latest_time=Max('created_time') ).values('latest_time') # 子查询2:根据最新时间,获取对应的message和created_time latest_details = TicketDetails.objects.filter( ticket=OuterRef('pk'), created_time=Subquery(latest_created_time) ).values('message', 'created_time') # 最终查询:关联Ticket表,获取所有需要的字段 result = Ticket.objects.annotate( message=Subquery(latest_details.values('message')[:1]), created_time=Subquery(latest_details.values('created_time')[:1]) ).values('id', 'name', 'type', 'user', 'message', 'created_time')
特殊情况处理
如果存在同一个ticket_id下有多条时间完全相同的消息,上面的方法会返回多条结果。如果你想确保每个Ticket只返回一条,可以改成按created_time倒序+id倒序取第一条:
# 直接按时间和id倒序,取第一条最新记录 latest_details = TicketDetails.objects.filter( ticket=OuterRef('pk') ).order_by('-created_time', '-id').values('message', 'created_time')[:1] result = Ticket.objects.annotate( message=Subquery(latest_details.values('message')), created_time=Subquery(latest_details.values('created_time')) ).values('id', 'name', 'type', 'user', 'message', 'created_time')
方法二:原生SQL查询
如果你更熟悉SQL语法,也可以直接用原生SQL来实现:
result = Ticket.objects.raw(''' SELECT t.id, t.name, t.type, t.user, td.message, td.created_time FROM ticket t JOIN ticket_details td ON t.id = td.ticket_id WHERE (td.ticket_id, td.created_time) IN ( SELECT ticket_id, MAX(created_time) FROM ticket_details GROUP BY ticket_id ) ''')
这个SQL的思路是:先通过子查询分组找到每个ticket_id对应的最新时间,再关联筛选出对应的消息记录,最后和Ticket表关联返回所有字段。
结果验证
运行上面的代码后,result里的每条数据就对应一个Ticket加上它的最新一条TicketDetails记录,和你期望的输出完全一致~
内容的提问来源于stack exchange,提问作者Sohan Joshi
相关产品推荐
相关产品推荐

