Django+Javascript动态生成ModelForm问题:JS无法识别Python变量
解决Django模板中JS动态添加表单失效的问题
问题场景
想通过点击按钮动态添加project_members_form表单,但点击后功能完全无反应。原模板和视图代码如下:
模板代码
{% extends 'base.html' %} {% block content %} <form method='POST'> <script> function addMemberFnc() { document.getElementById("teamMemberDiv").innerHTML+={{ project_members_form }} } </script> {{ project_form }} <p id="demo" onclick="addMemberFnc()">Add member.</p> <div style="border-style: dotted" id="teamMemberDiv"> {# {{ project_members_form }}#} </div> <button type="submit" class="btn btn-primary my-5">Submit</button> </form> {% endblock %}
视图代码
def project_generation(request): if request.method == 'POST': pass else: project_form = ProjectForm(prefix='Project') project_role_form = ProjectRoleForm(prefix='ProjectRole') project_members_form = ProjectMemberForm(prefix='ProjectMember') return render(request, "project_xml_generation.html", { "project_form": project_form, "project_members_form": project_members_form }, )
错误原因
Django渲染{{ project_members_form }}时会直接输出原始HTML代码,把这段HTML拼到JS的innerHTML里会破坏JS语法结构——比如HTML里的双引号、换行符会导致JS解析报错(浏览器控制台会出现类似Unexpected token '<'的错误),最终函数无法执行。
解决方案
1. 转义表单HTML为合法JS字符串
用Django的escapejs过滤器,把表单内容转义成JS能识别的字符串格式,避免语法错误。
2. 动态生成表单唯一前缀
多个同前缀的表单提交时会互相覆盖数据,所以每次新增表单要修改前缀,生成唯一标识(比如加数字后缀),确保后端能正确识别每个表单的提交数据。
修改后的代码
模板代码
{% extends 'base.html' %} {% block content %} <form method='POST'> {% csrf_token %} <!-- 必须添加,否则POST提交会被Django的CSRF保护拦截 --> <script> // 提前把表单HTML转义成合法JS字符串 const baseMemberForm = "{{ project_members_form|escapejs }}"; let memberIndex = 1; // 用来生成唯一前缀 function addMemberFnc() { // 替换表单中的前缀,生成唯一标识 const newForm = baseMemberForm.replace(/ProjectMember/g, `ProjectMember_${memberIndex}`); document.getElementById("teamMemberDiv").innerHTML += newForm; memberIndex++; } </script> {{ project_form }} <p id="demo" onclick="addMemberFnc()">添加成员</p> <div style="border-style: dotted" id="teamMemberDiv"> </div> <button type="submit" class="btn btn-primary my-5">提交</button> </form> {% endblock %}
视图代码(处理POST请求)
def project_generation(request): if request.method == 'POST': project_form = ProjectForm(request.POST, prefix='Project') member_forms = [] current_index = 0 # 遍历所有提交的成员表单 while f'ProjectMember_{current_index}-id' in request.POST: form = ProjectMemberForm(request.POST, prefix=f'ProjectMember_{current_index}') member_forms.append(form) current_index += 1 # 验证所有表单并保存 if project_form.is_valid() and all(form.is_valid() for form in member_forms): project = project_form.save() for form in member_forms: member = form.save(commit=False) member.project = project # 关联到当前项目 member.save() # 保存成功后重定向到列表页或成功页面 return redirect('project_list') else: project_form = ProjectForm(prefix='Project') project_role_form = ProjectRoleForm(prefix='ProjectRole') project_members_form = ProjectMemberForm(prefix='ProjectMember') return render(request, "project_xml_generation.html", { "project_form": project_form, "project_members_form": project_members_form }, )
关键说明
escapejs过滤器会自动转义HTML中的特殊字符(比如双引号、换行符),让表单HTML能作为合法的JS字符串使用。- 修改前缀是Django处理多个同类型表单的标准方式,确保每个表单的数据在提交时不会被覆盖,后端能正确解析每个表单的字段。
内容的提问来源于stack exchange,提问作者JohnyCapo
相关产品推荐
相关产品推荐

