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

Django多模型关联下如何实现按月精准过滤功能?

问题:Django关联模型过滤后仍显示全部关联数据

在Django中定义了关联的Profil和Posyandu模型,尝试基于Posyandu的bulan字段实现过滤时,选择月份后仍会展示该Profil下所有的Posyandu数据,而非仅选中月份的记录。


现有代码

模型定义

from django.db import models

class Profil(models.Model):
    nik     = models.IntegerField()
    nama    = models.CharField(max_length=40)
    tgl     = models.DateField()
    kelamin = models.CharField(max_length=10)
    namaibu = models.CharField(max_length=40)
    nikibu  = models.IntegerField()
    dusun  = models.CharField(max_length=30, default='Sumberejo')

    def __str__(self):
        return f"{self.nik}, {self.nama}, {self.namaibu}"
    
class Posyandu(models.Model):
    bulan  = models.CharField(max_length=20, default='Januari')
    nik = models.ForeignKey(Profil, on_delete=models.CASCADE)
    tb  = models.IntegerField()
    bb  = models.IntegerField()
    ll  = models.IntegerField()
    lk  = models.IntegerField()
    ket = models.CharField(max_length=20,)

    def __str__(self):
        return f"{self.nik.nama}, {self.bulan}, {self.tb}, {self.bb}"

展示模板

<table>
    <thead>
        <tr>
            <th>NIK</th>
            <th>Nama</th>
            <th>Tanggal Lahir</th>
            <th>Kelamin</th>
            <th>Nama Ibu</th>
            <th>Bulan</th>
            <th>TB</th>
            <th>BB</th>
            <th>LL</th>
            <th>LK</th>
            <th>Keterangan</th>
        </tr>
    </thead>
    <tbody>
        {% for profil in profils %}
        {% for posyandu in profil.posyandu_set.all %}
        <tr>
            <td>{{ profil.nik }}</td>
            <td>{{ profil.nama }}</td>
            <td>{{ profil.tgl }}</td>
            <td>{{ profil.kelamin }}</td>
            <td>{{ profil.namaibu }}</td>
            <td>{{ posyandu.bulan }}</td>
            <td>{{ posyandu.tb }}</td>
            <td>{{ posyandu.bb }}</td>
            <td>{{ posyandu.ll }}</td>
            <td>{{ posyandu.lk }}</td>
            <td>{{ posyandu.ket }}</td>
        </tr>
        {% endfor %}
        {% empty %}
        <tr>
            <td colspan="11">No data available</td>
        </tr>
        {% endfor %}
    </tbody>
</table>

视图与表单

# 视图
def riwayat(response):
    profils = Profil.objects.prefetch_related('posyandu_set')
    if response.method == 'POST':
        forms = BulanForm(response.POST)
        if forms.is_valid():
            bulan = forms.cleaned_data['bulan']
            profils = profils.filter(posyandu__bulan=bulan)
    else:
        forms = BulanForm()
    return render(response, 'all.html', {'profils': profils, 'forms' : forms})

# 表单
class BulanForm(forms.Form):
    pilihanbulan = [
    ('Januari', 'Januari'),
    ('Februari', 'Februari'),
    ('Maret', 'Maret'),
    ('April', 'April'),
    ('Mei', 'Mei'),
    ('Juni', 'Juni'),
    ('Juli', 'Juli'),
    ('Agustus', 'Agustus'),
    ('September', 'September'),
    ('Oktober', 'Oktober'),
    ('November', 'November'),
    ('Desember', 'Desember'),
]

    bulan = forms.ChoiceField(
        required = True,
        widget = forms.Select,
        choices = pilihanbulan,
        label = 'Posyandu Bulan'
    )

问题原因

当前视图中filter(posyandu__bulan=bulan)仅筛选出存在目标月份Posyandu记录的Profil对象,但prefetch_related('posyandu_set')会拉取这些Profil的所有关联Posyandu数据,因此模板遍历profil.posyandu_set.all时仍会显示全部记录。


解决方法

方法一:用Prefetch过滤预取的关联数据

保留原有模板结构,通过Prefetch对象指定仅预取目标月份的Posyandu数据:

from django.db.models import Prefetch

def riwayat(response):
    profils = Profil.objects.all()
    if response.method == 'POST':
        forms = BulanForm(response.POST)
        if forms.is_valid():
            bulan = forms.cleaned_data['bulan']
            # 过滤目标月份的Posyandu查询集
            filtered_posyandu = Posyandu.objects.filter(bulan=bulan)
            # 关联过滤后的Posyandu数据
            profils = profils.prefetch_related(
                Prefetch('posyandu_set', queryset=filtered_posyandu)
            ).filter(posyandu__bulan=bulan)
    else:
        forms = BulanForm()
        # 默认预取所有Posyandu数据
        profils = profils.prefetch_related('posyandu_set')
    return render(response, 'all.html', {'profils': profils, 'forms' : forms})

方法二:直接查询Posyandu模型(更高效)

直接查询符合条件的Posyandu记录并关联Profil,同时调整模板遍历逻辑:

  1. 修改视图:
def riwayat(response):
    posyandus = Posyandu.objects.select_related('nik').all()
    if response.method == 'POST':
        forms = BulanForm(response.POST)
        if forms.is_valid():
            bulan = forms.cleaned_data['bulan']
            posyandus = posyandus.filter(bulan=bulan)
    else:
        forms = BulanForm()
    return render(response, 'all.html', {'posyandus': posyandus, 'forms' : forms})
  1. 修改模板:
<table>
    <thead>
        <tr>
            <th>NIK</th>
            <th>Nama</th>
            <th>Tanggal Lahir</th>
            <th>Kelamin</th>
            <th>Nama Ibu</th>
            <th>Bulan</th>
            <th>TB</th>
            <th>BB</th>
            <th>LL</th>
            <th>LK</th>
            <th>Keterangan</th>
        </tr>
    </thead>
    <tbody>
        {% for posyandu in posyandus %}
        <tr>
            <td>{{ posyandu.nik.nik }}</td>
            <td>{{ posyandu.nik.nama }}</td>
            <td>{{ posyandu.nik.tgl }}</td>
            <td>{{ posyandu.nik.kelamin }}</td>
            <td>{{ posyandu.nik.namaibu }}</td>
            <td>{{ posyandu.bulan }}</td>
            <td>{{ posyandu.tb }}</td>
            <td>{{ posyandu.bb }}</td>
            <td>{{ posyandu.ll }}</td>
            <td>{{ posyandu.lk }}</td>
            <td>{{ posyandu.ket }}</td>
        </tr>
        {% empty %}
        <tr>
            <td colspan="11">No data available</td>
        </tr>
        {% endfor %}
    </tbody>
</table>

方案对比

  • 方法一:保留原模板的Profil层级结构,适合需要按用户分组展示记录的场景;
  • 方法二:查询更高效,直接获取目标数据,减少不必要的关联拉取,适合仅展示符合条件的体检记录的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 13:44:56