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,同时调整模板遍历逻辑:
- 修改视图:
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})
- 修改模板:
<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_
相关产品推荐
相关产品推荐

