Django中如何根据关联模型状态在列表展示对应操作链接
问题描述
我做了一个带搜索框的用户列表,列表项里包含审批申请的超链接。需求是:对已批准申请的用户显示“已批准”按钮,未批准的显示“批准”链接。我尝试在循环里加条件判断,但现在未批准用户的“批准”按钮会重复显示多次,已批准用户则同时显示“批准”和“已批准”按钮。我是Django新手,卡在这里了,求解决办法。
现有代码
Models代码
class Fee(models.Model): applicant = models.OneToOneField(User, on_delete=models.CASCADE, null=True) email = models.CharField(max_length=30, null=True) phone = models.CharField(max_length=20, null=True) date = models.DateTimeField(auto_now_add=True) def __str__(self): return f'Payments: {self.applicant}' class Profile(models.Model): applicant = models.OneToOneField(User, on_delete=models.CASCADE, null = True) surname = models.CharField(max_length=20, null=True) othernames = models.CharField(max_length=40, null=True) gender = models.CharField(max_length=6, choices=GENDER, blank=True, null=True) def __str__(self): return f'{self.applicant.username}-Profile'
Views代码
def search_applicants(request): # 初始化空上下文 context = {} # 搜索表单 searchForm = SearchApplicantForm(request.GET or None) payments = Fee.objects.all() if searchForm.is_valid(): # 获取搜索值 value = searchForm.cleaned_data['value'] # 用Q对象按姓氏或其他名称过滤 user_filter = Q(surname__icontains = value) | Q(othernames__icontains = value) # 应用过滤条件到Profile对象 list_submited = Profile.objects.filter(user_filter) else: list_submited = Profile.objects.all() paginator = Paginator(list_submited, 5) page = request.GET.get('page') paged_list_submited = paginator.get_page(page) # 更新上下文变量 context.update({ 'list_applicants':paged_list_submited, 'searchForm':searchForm, 'payments':payments, }) return render(request, 'user/list_applicants.html', context)
HTML模板代码
<table class="table bg-white"> <thead class="bg-info text-white"> <tr> <th scope="col">#</th> <th scope="col">姓氏</th> <th scope="col">其他名称</th> <th scope="col">邮箱</th> <th scope="col">手机号</th> <th scope="col">操作</th> </tr> </thead> {% if list_applicants %} <tbody> {% for applicant in list_applicants %} <tr> <td>{{ forloop.counter }}</td> <td>{{ applicant.surname }}</td> <td>{{ applicant.othernames }}</td> <td>{{ applicant.applicant.email }}</td> <td>{{ applicant.applicant.profile.phone }}</td> <th> {% for fee in payments %} {% if fee.applicant == applicant.applicant %} <a class="btn btn-info btn-sm">已批准</a> {% else %} <a class="btn btn-info btn-sm" href="{% url 'approve-applicant' applicant.id %}">批准</a> {% endif %} {% endfor %} </th> </tr> {% endfor %} </tbody> {% else %} 暂无记录 {% endif %} </table>
问题原因
你当前的模板循环了所有payments记录,每一条Fee都会生成一个按钮:
- 已批准用户:循环到他对应的
Fee时显示“已批准”,但循环到其他Fee时,因不匹配会显示“批准”,导致同时出现两个按钮。 - 未批准用户:每一条
Fee都会触发else分支,生成一个“批准”按钮,按钮数量等于Fee表的总记录数。
解决方案
核心修复:去掉冗余循环,直接判断用户审批状态
因为Fee和User是OneToOne关系,可通过反向关联直接判断当前用户是否存在审批记录,无需循环所有payments:
修改HTML模板中操作列的代码:
<th> {% if applicant.applicant.fee %} <a class="btn btn-info btn-sm">已批准</a> {% else %} <a class="btn btn-info btn-sm" href="{% url 'approve-applicant' applicant.id %}">批准</a> {% endif %} </th>
同时可以删除视图中payments = Fee.objects.all()和上下文里的'payments':payments,因为不再需要这个变量。
可选优化:提升查询性能
如果用户数量较多,为避免模板中出现N+1查询问题,可在视图中提前预取关联数据:
if searchForm.is_valid(): value = searchForm.cleaned_data['value'] user_filter = Q(surname__icontains = value) | Q(othernames__icontains = value) # 预取关联的User及对应的Fee list_submited = Profile.objects.filter(user_filter).select_related('applicant__fee') else: list_submited = Profile.objects.all().select_related('applicant__fee')
额外优化:简化模板字段调用
模板里{{ applicant.applicant.profile.surname }}可直接写成{{ applicant.surname }},因为applicant本身就是Profile对象,无需绕路调用。
内容的提问来源于stack exchange,提问作者apollos
相关产品推荐
相关产品推荐

