You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 %}

原理说明

  1. 模型关联逻辑:Django会为外键自动创建反向关联,通过related_name指定的名称(如checkins),可以直接从WaiverAdult或WaiverMinor实例访问对应的签到记录。
  2. 预取关联优化:prefetch_related和Prefetch对象可以一次性从数据库拉取所有关联数据,避免模板循环时反复查询数据库(N+1查询问题),大幅提升页面加载性能。
  3. 模板查询逻辑:
    • exists()用于判断当前用户是否有签到记录,避免空指针异常;
    • latest('checkintime')用于获取最新的签到记录;
    • {% empty %}分支处理无签到记录的场景,保证页面显示完整性。

内容的提问来源于stack exchange,提问作者cbirch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 22:45:39