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

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:通过过滤条件重建当前表格课程集合(更优,无冗余)

因为表格的课程集合完全由过滤条件决定,不需要额外传递字段,直接在后端通过过滤参数重建即可:

  1. 修改视图,先获取过滤条件再传给更新函数:
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})
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 05:43:23