如何为GroupCaseFile标注含最新ProceduralAct的字段并实现多字段搜索?
问题需求
我需要在HTML模板的表格中展示GroupCaseFile模型的数据,其中某一列要显示对应最新ProceduralAct对象的act_number、act_type和summary字段(放在同一个单元格里)。
原本可以通过group_case_file.case_file.proceduralact_set.all().order_by('-created_at').first()获取这个最新对象,但现在需要先根据搜索词对最新ProceduralAct的这三个字段进行过滤,再获取符合条件的GroupCaseFile数据。
我试过用嵌套子查询标注summary字段来实现单字段搜索,但想知道能不能标注一个名为last_procedural_act的字段,让它包含对应最新ProceduralAct的查询集,从而实现多字段搜索;同时也希望得到其他可行的实现方案。
模型代码
class CaseFile(models.Model): code = models.CharField(primary_key=True, max_length=30) # 其他字段 class GroupCaseFile(models.Model): id = models.BigAutoField(primary_key=True) case_file = models.ForeignKey( CaseFile, on_delete=models.CASCADE, default=None) group = models.ForeignKey('accounts.Group', on_delete=models.CASCADE) client_name = models.CharField(max_length=255, null=True, blank=True) # 其他字段 class ProceduralAct(models.Model): id = models.BigAutoField(primary_key=True) case_file = models.ForeignKey( CaseFile, on_delete=models.CASCADE, default=None) act_type = models.CharField(max_length=255) act_number = models.CharField(max_length=255, null=True) summary = models.TextField() created_at = models.DateField()
现有视图代码片段
# 获取最新程序性动作的summary的子查询 subquery_procedural_acts_summary = Subquery( ProceduralAct.objects.filter( casefile=OuterRef('pk') # 注:原代码字段名错误,应为case_file ).order_by('-created_at').values('summary')[:1] ) # 通过CaseFile关联获取最新summary的子查询 subquery_case_file_last_procedural_act_summary = Subquery( CaseFile.objects.annotate( last_procedural_act_summary=subquery_procedural_acts_summary ).filter( groupcasefile=OuterRef('pk') # 注:原代码关联逻辑错误,需按反向关联调整 ).values('last_procedural_act_summary') ) # 给GroupCaseFile标注最新summary字段 group_case_files = GroupCaseFile.objects.annotate( last_procedural_act_summary=subquery_case_file_last_procedural_act_summary ) # 待加入搜索过滤逻辑 dashboard_case_files_list = [] for group_case_file in group_case_files: # 组装数据传到模板
解决方案
方案一:标注多字段子查询实现数据库层面过滤
无法直接标注包含完整ProceduralAct对象的字段,但可以分别标注最新动作的三个核心字段,既能支持多字段搜索过滤,也能直接在模板中展示:
from django.db.models import OuterRef, Subquery, Q # 先获取每个CaseFile对应的最新ProceduralAct的ID(通过ID关联取字段更高效) subquery_latest_pa_id = Subquery( ProceduralAct.objects.filter( case_file=OuterRef('case_file__pk') # GroupCaseFile关联CaseFile的主键 ).order_by('-created_at').values('pk')[:1] ) # 给GroupCaseFile标注最新动作的三个字段 group_case_files = GroupCaseFile.objects.annotate( latest_pa_id=subquery_latest_pa_id, latest_pa_act_number=Subquery( ProceduralAct.objects.filter(pk=OuterRef('latest_pa_id')).values('act_number')[:1] ), latest_pa_act_type=Subquery( ProceduralAct.objects.filter(pk=OuterRef('latest_pa_id')).values('act_type')[:1] ), latest_pa_summary=Subquery( ProceduralAct.objects.filter(pk=OuterRef('latest_pa_id')).values('summary')[:1] ) ) # 多字段搜索过滤示例(search_term为用户输入的关键词) search_term = request.GET.get('search', '') if search_term: group_case_files = group_case_files.filter( Q(latest_pa_act_number__icontains=search_term) | Q(latest_pa_act_type__icontains=search_term) | Q(latest_pa_summary__icontains=search_term) ) # 模板中单个单元格展示方式: # <td>{{ obj.latest_pa_act_number }} - {{ obj.latest_pa_act_type }}<br>{{ obj.latest_pa_summary }}</td>
方案二:用Window函数+Prefetch优化查询
借助Django的Window函数标记每个CaseFile的最新动作,再通过Prefetch一次性拉取关联数据,适合大数据量场景:
from django.db.models import Window, F, Q from django.db.models.functions import RowNumber # 给ProceduralAct按CaseFile分组,按created_at倒序标记行号,只保留每个CaseFile的最新动作 pa_with_row_number = ProceduralAct.objects.annotate( row_num=Window( expression=RowNumber(), partition_by=F('case_file'), order_by=F('created_at').desc() ) ).filter(row_num=1) # 用Prefetch将GroupCaseFile与最新ProceduralAct关联,避免N+1查询 group_case_files = GroupCaseFile.objects.prefetch_related( Prefetch( 'case_file__proceduralact_set', queryset=pa_with_row_number, to_attr='latest_procedural_act' ) ) # 搜索过滤逻辑 if search_term: target_pas = pa_with_row_number.filter( Q(act_number__icontains=search_term) | Q(act_type__icontains=search_term) | Q(summary__icontains=search_term) ) group_case_files = group_case_files.filter( case_file__proceduralact__in=target_pas ).distinct() # 去重避免重复匹配 # 模板中展示方式: # {% for obj in group_case_files %} # <td> # {% with latest_pa=obj.case_file.latest_procedural_act.0 %} # {{ latest_pa.act_number }} - {{ latest_pa.act_type }}<br>{{ latest_pa.summary }} # {% endwith %} # </td> # {% endfor %}
方案三:自定义模型属性(仅适合小数据量)
如果数据量不大,可给GroupCaseFile添加属性直接获取最新动作,但此方式无法在数据库层面过滤,需在内存中处理:
# 在GroupCaseFile模型中添加属性 class GroupCaseFile(models.Model): # 原有字段... @property def last_procedural_act(self): return self.case_file.proceduralact_set.order_by('-created_at').first() # 视图中拉取数据并内存过滤 group_case_files = GroupCaseFile.objects.all().select_related('case_file') if search_term: group_case_files = [ gcf for gcf in group_case_files if gcf.last_procedural_act and ( search_term in (gcf.last_procedural_act.act_number or '') or search_term in gcf.last_procedural_act.act_type or search_term in gcf.last_procedural_act.summary ) ] # 模板中直接调用: # <td>{{ obj.last_procedural_act.act_number }} - {{ obj.last_procedural_act.act_type }}<br>{{ obj.last_procedural_act.summary }}</td>
内容的提问来源于stack exchange,提问作者Rohalt
相关产品推荐
相关产品推荐

