Django中如何在POST请求时获取渲染表单表格的过滤课程集合?
问题描述
我有一个Django表单,里面是对应数据库Course对象的表格,每行带复选框。表单除了通用提交按钮,还有几个用于过滤表格课程的提交按钮。这个表单的作用是更新用户Profile的interests多对多关联:提交时,过滤结果里勾选的课程要加到关联中,未勾选的要从关联里移除。
我能拿到勾选的课程,但需要明确表格里显示的课程集合(表格可能只展示过滤后的课程)——也就是渲染时传入HTML上下文的课程,但不知道怎么在POST请求处理阶段获取。我知道可以用隐藏输入(每行加含课程reference的隐藏字段)实现,但想确认这是不是最优方案,或者有没有更好的解法。重点看update_user_interests()函数,怎么获取表单表格上下文里的课程?
表单代码(HTML模板)
<form method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" name="submit" value='Submit'/> {% for filter in filters%} <input type="submit" name="filter" value='{{filter}}'/> {% endfor %} <table class="table table-striped table-bordered"> <tbody> {% for course in courses %} <tr> <td> <input type="checkbox" name="checks" value={{course.reference}} {% if course in user_interests %} checked {% endif %}> </td> <td> {{ course.reference|default:"" }} </td> <td> {{ course.name|default:"" }} </td> <td> {{ course.description|default:"" }} </td> </tr> {% endfor %} </tbody> </table> </form>
视图代码
def table(request): profile = Profile.objects.get(user=request.user.id) allcourses = Course.objects.all() filteropts = ['A','B','C'] if request.method == 'GET': tablecourses = allcourses user_interests = profile.interests.all() if request.method == 'POST': # 更新用户兴趣 update_user_interests(request, profile) user_interests = profile.interests.all() # 根据过滤按钮点击结果筛选表格 filter = get_filter(request) if filter: tablecourses = Course.objects.filter(curriculum=filter).values() else: tablecourses = allcourses return render(request, 'table.html', {'courses': tablecourses, 'user_interests' : user_interests, 'filters':filteropts})
辅助函数代码
def get_filter(request): if 'filter' in request.POST: return request.POST.getlist("filter")[0] def update_user_interests(request, profile): tablecourses = [] # 怎么获取表单表格里的课程? checked_courses = request.POST.getlist("checks") for course in tablecourses: ref = course.reference if ref in checked_courses: profile.interests.add(coursemodel) else: profile.interests.remove(coursemodel) profile.save()
模型代码
class Course(models.Model): name = models.CharField( unique=False, null=False, blank=False, max_length=250 ) reference = models.CharField( unique=True, null=False, blank=False, max_length=250 ) curriculum = models.CharField( unique=False, null=True, blank=True, max_length=250, ) description = models.TextField( unique=False, null=True, blank=True, max_length=2000, ) class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) interests = models.ManyToManyField( Course, related_name='interests')
解决方案
方案1:隐藏字段传递当前表格课程集合(直接但有冗余)
在表格的每一行添加隐藏字段,把当前显示的课程reference全部传回后端:
{% for course in courses %} <tr> <td> <input type="checkbox" name="checks" value="{{course.reference}}" {% if course in user_interests %} checked {% endif %}> <!-- 添加隐藏字段记录当前行课程的reference --> <input type="hidden" name="table_courses" value="{{course.reference}}"> </td> <!-- 其他列代码不变 --> </tr> {% endfor %}
然后修改update_user_interests函数获取课程集合:
def update_user_interests(request, profile): # 获取当前表格所有课程的reference table_course_refs = request.POST.getlist("table_courses") # 通过reference查询对应的Course对象 tablecourses = Course.objects.filter(reference__in=table_course_refs) checked_courses = request.POST.getlist("checks") for course in tablecourses: if course.reference in checked_courses: profile.interests.add(course) else: profile.interests.remove(course) profile.save()
这个方案逻辑简单,但如果表格课程数量大,会增加POST请求的数据量。
方案2:通过过滤条件重建当前表格课程集合(更优,无冗余)
因为表格的课程集合完全由过滤条件决定,不需要额外传递字段,直接在后端通过过滤参数重建即可:
- 修改视图,先获取过滤条件再传给更新函数:
def table(request): profile = Profile.objects.get(user=request.user.id) allcourses = Course.objects.all() filteropts = ['A','B','C'] if request.method == 'GET': tablecourses = allcourses user_interests = profile.interests.all() if request.method == 'POST': # 先获取当前过滤条件 current_filter = get_filter(request) # 把过滤条件传给更新函数 update_user_interests(request, profile, current_filter) user_interests = profile.interests.all() # 筛选表格课程 if current_filter: tablecourses = Course.objects.filter(curriculum=current_filter) else: tablecourses = allcourses return render(request, 'table.html', {'courses': tablecourses, 'user_interests' : user_interests, 'filters':filteropts})
- 修改
update_user_interests函数,根据过滤条件重建课程集合,并使用批量操作优化性能:
def update_user_interests(request, profile, current_filter): # 根据过滤条件重建当前表格展示的课程 if current_filter: tablecourses = Course.objects.filter(curriculum=current_filter) else: tablecourses = Course.objects.all() checked_course_refs = request.POST.getlist("checks") # 批量获取要添加和移除的课程 to_add = tablecourses.filter(reference__in=checked_course_refs) to_remove = tablecourses.exclude(reference__in=checked_course_refs) # 批量操作比循环逐个处理效率更高 profile.interests.add(*to_add) profile.interests.remove(*to_remove) profile.save()
这个方案无需额外字段,减少POST数据量,且批量操作能大幅提升性能,是更优的选择。
注意事项
- 要确保视图和
update_user_interests中的过滤逻辑完全一致,避免后端重建的课程集合和前端展示的不一致。 - 批量操作
add(*to_add)和remove(*to_remove)仅适用于Django 2.0+版本,低版本需调整为循环处理。
内容的提问来源于stack exchange,提问作者user22574281
相关产品推荐
相关产品推荐

