Django多表关联查询:如何在模板展示成人、未成年人及对应签到时间
问题分析与解决方案
首先你的模型定义存在两处错误:CheckIns的外键指向了不存在的WaiverParent和WaiverChild,需修正为关联WaiverAdult和WaiverMinor;同时建议为外键添加related_name,让反向查询更直观。修正后的模型如下:
class WaiverAdult(models.Model): first_name = models.CharField(max_length=50, blank=True) class WaiverMinor(models.Model): first_name = models.CharField(max_length=50, blank=True) parent = models.ForeignKey(WaiverAdult, on_delete=models.CASCADE) class CheckIns(models.Model): # 添加related_name,方便反向查询签到记录 adult = models.ForeignKey(WaiverAdult, on_delete=models.CASCADE, blank=True, null=True, related_name='checkins') minor = models.ForeignKey(WaiverMinor, on_delete=models.CASCADE, blank=True, null=True, related_name='checkins') checkintime = models.DateTimeField(auto_now_add=True)
接下来针对你的需求,分步骤解决模板中无法显示签到时间的问题:
1. 调整视图查询集,预取关联数据
当前视图仅预取了未成年人数据,但未关联CheckIns,会导致模板中查询签到记录时产生N+1性能问题。修改视图,同时预取成人、未成年人对应的签到记录:
from django.db.models import Prefetch class WaiverListView(ListView): queryset = WaiverAdult.objects.all().prefetch_related( # 预取成人的签到记录 'checkins', # 预取未成年人,并同时预取每个未成年人的签到记录 Prefetch( 'waiverminor_set', queryset=WaiverMinor.objects.prefetch_related('checkins'), to_attr='prefetched_minors' # 自定义属性名,避免和默认查询集冲突 ) ).order_by('id') # 替换为你实际需要的排序字段,比如created context_object_name = "waiver_list" template_name = 'waiver/waiver_list.html'
2. 模板修改:显示签到时间
根据你需要的输出格式,分两种场景处理:
场景1:每个用户仅显示最新签到时间
如果只需要展示成人/未成年人的最新签到记录,模板代码如下:
{% for adult in waiver_list %} <tr> <td>{{ adult.first_name }}</td> <td> {% if adult.checkins.exists %} {{ adult.checkins.latest('checkintime').checkintime }} {% else %} 未签到 {% endif %} </td> </tr> {% for child in adult.prefetched_minors %} <tr> <td>{{ child.first_name }}</td> <td> {% if child.checkins.exists %} {{ child.checkins.latest('checkintime').checkintime }} {% else %} 未签到 {% endif %} </td> </tr> {% endfor %} {% endfor %}
场景2:列出用户所有签到记录(每条记录单独一行)
如果需要像示例那样,每条签到记录对应一行,模板代码调整为:
{% for adult in waiver_list %} {% for checkin in adult.checkins.all %} <tr> <td>{{ adult.first_name }}</td> <td>{{ checkin.checkintime }}</td> </tr> {% empty %} <!-- 可选:无签到记录时的提示 --> <tr> <td>{{ adult.first_name }}</td> <td>未签到</td> </tr> {% endfor %} {% for child in adult.prefetched_minors %} {% for checkin in child.checkins.all %} <tr> <td>{{ child.first_name }}</td> <td>{{ checkin.checkintime }}</td> </tr> {% empty %} <tr> <td>{{ child.first_name }}</td> <td>未签到</td> </tr> {% endfor %} {% endfor %} {% endfor %}
原理说明
- 模型关联逻辑:Django会为外键自动创建反向关联,通过
related_name指定的名称(如checkins),可以直接从WaiverAdult或WaiverMinor实例访问对应的签到记录。 - 预取关联优化:
prefetch_related和Prefetch对象可以一次性从数据库拉取所有关联数据,避免模板循环时反复查询数据库(N+1查询问题),大幅提升页面加载性能。 - 模板查询逻辑:
exists()用于判断当前用户是否有签到记录,避免空指针异常;latest('checkintime')用于获取最新的签到记录;{% empty %}分支处理无签到记录的场景,保证页面显示完整性。
内容的提问来源于stack exchange,提问作者cbirch
相关产品推荐
相关产品推荐

