Django模板表单提交按钮失效问题排查与解决请求
问题背景
使用Django 3.0和Python 3.7开发时,模板中的表单提交按钮点击后无响应,未触发任何后端操作。以下是相关代码及排查、解决步骤:
相关代码片段
views.py(sendmail视图)
def sendmail(request): company={} form = JobEmailDataForm() client = request.user.client client_id = request.user.client.id email_data = JobEmailData.objects.filter(client_id=client) for i in email_data: reminderstatus = i.reminder_status bookingstatus = i.booking_today_status purchasing_officer = i.purchasing_officer bookedby = request.user client_data = Client.objects.filter(id=client_id) for val in client_data: company['name']=val.name company['phone']=val.phone company['email']=val.email company['replay_to_email']=val.replay_to_email or '' company['website']=val.website company['logo'] = val.logo_url company['address'] = get_client_address(client) return render(request, 'core/job_email_form_1.html', locals())
forms.py(JobEmailDataForm表单类)
class JobEmailDataForm(forms.Form): name = forms.CharField(max_length=100, label='Company Name', widget=forms.TextInput(attrs={'class':'input-xlarge'})) address = forms.CharField(max_length=256, label='Company Address', widget=forms.TextInput(attrs={'class':'input-xlarge'})) phone = forms.CharField(max_length=100, label='Company Phone', widget=Html5TelephoneInput(attrs={'class':'input-xlarge'})) website = forms.URLField(max_length=100, label='Company Website', required=False, widget=forms.TextInput(attrs={'class':'input-xlarge'})) email = forms.EmailField(label='Company Email', widget=Html5EmailInput(attrs={'class':'input-xlarge'})) replay_to_email = forms.EmailField(label='Send from Email (Optional)', required=False, widget=Html5EmailInput(attrs={'class':'input-xlarge'})) purchasing_officer = forms.EmailField(label='Purchasing Officer Email', required=False, widget=Html5EmailInput(attrs={'class':'input-xlarge'})) logo = forms.ImageField(label='Image', required=False) confirmation = forms.CharField(max_length=256, label='Additional Booking Confirmation Message', widget=forms.Textarea) amendments = forms.CharField(max_length=256, label='Additional Booking Amendments Message', widget=forms.Textarea) cancellation = forms.CharField(max_length=256, label='Additional Booking Cancellation Message', widget=forms.Textarea) booking_today = forms.CharField(max_length=256, label='Additional Booking today Message', widget=forms.Textarea) reminder = forms.CharField(max_length=256, label='Additional Booking Reminders Message', widget=forms.Textarea) reminder_status = forms.ChoiceField(choices=REMINDER_CHOICES,label='Reminders Message Settings',) booking_today_status = forms.ChoiceField(choices=REMINDER_CHOICES,label='Booking Today Message Settings',) class Meta: widgets = { "confirmation": forms.TextInput, } fields = ['name', 'address', 'phone', 'website','email', 'replay_to_email' , 'logo','confirmation','amendments','cancellation','reminder','is_email_customer','is_email_user','reminder_status']
job_email.html模板
<div class="row"> <div class="span6 offset1"> <!-- START non-filtered --> <form method="post" enctype="multipart/form-data" action="/saveemaildata/"> {% csrf_token %} {% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %} <fieldset class="add"> <br><br> {% for field in form.visible_fields %} <div class="control-group"> <label class="control-label pull-left" for="{{ field.auto_id }}">{{ field.label }}</label> <div class="controls" style="margin-left: 0px;"> {{ field }} {% if field.label == 'Send from Email (Optional)' %}{% if client.email_validated_status == 'processed' or client.email_validated_status == 'Approved by client' %} Processed <a href="#EmailValidateModal" data-toggle="modal">Check now</a> {% elif client.email_validated_status == 'Validated' %} Validated<a href="#EmailValidateModal" data-toggle="modal">Check now</a> {% elif client.email_validated_status == 'Pending' %} Validate - <a href="#EmailValidateModal" data-toggle="modal">Check how</a> {% endif %}{% endif %} </div> <p>{{ field.help_text }}</p> </div> {% endfor %} </fieldset> <div id="form-buttons-container" class="form-actions"> <div class="controls"> <input type="submit" class="btn btn-inverse" value="Update JobEmails"> <a class="btn btn-default" href="{% url "roles_list" %}">Cancel</a> {% if role %} <a class="btn btn-danger" href="{% url "roles_delete" role.pk %}">Delete role</a> {% endif %} </div> <!--/form-actions--> </div> </form> ..... ..... ..... </div> <!-- /row -->
urls.py配置
re_path(r'^saveemaildata/$',savejob,name='save_email_data'),
savejob处理视图
def savejob(request): messages = [] client = request.user.client if request.POST: companyName = request.POST['name'] companyAddress = request.POST['address'] companyPhone = request.POST['phone'] companyWebsite = request.POST['website'] companyEmail = request.POST['email'] replay_to_email = request.POST['replay_to_email'] purchasing_officer = request.POST['purchasing_officer'] Confirmation = request.POST['confirmation'] Amendments = request.POST['amendments'] Cancellation = request.POST['cancellation'] Reminder = request.POST['reminder'] Bookingtoday = request.POST['booking_today'] reminder_status = request.POST['reminder_status'] booking_today_status = request.POST['booking_today_status'] client_id = ClientUser.objects.filter(id=request.user.id)[0].client_id doc = Client.objects.get(id=client_id) errors = [] import sys, traceback if replay_to_email: if doc.replay_to_email != replay_to_email: doc.email_validated_status = 'Pending' send_email_validate.delay(request.user, 1) doc.name = companyName doc.replay_to_email = replay_to_email doc.email = companyEmail doc.phone = companyPhone doc.website = companyWebsite if 'logo' in request.FILES: doc.logo_url = request.FILES['logo'] print("DOC LOGO URL IS",doc.logo_url) doc.logo_url.storage = S3Boto3Storage(bucket_name='lightdegree') print("DOC LOGO URLLL",doc.logo_url.storage) else: errors.append("There is no document specified") doc.save() template = 'core/savejob.html' objclient = Client.objects.get(name=companyName,email=companyEmail) clientid = objclient.id email_data = JobEmailData.objects.filter(client_id=clientid) if not email_data: objemail = JobEmailData(name = companyName,phone=companyPhone, website=companyWebsite,client_id=clientid, address=companyAddress, confirmation=Confirmation,amendments=Amendments, cancellation=Cancellation,reminder=Reminder,booking_today=Bookingtoday, purchasing_officer=purchasing_officer,reminder_status=reminder_status,booking_today_status=booking_today_status) objemail.save() else: objemail = JobEmailData.objects.filter(client_id=clientid).update(name = companyName,phone=companyPhone, website=companyWebsite, address=companyAddress, confirmation=Confirmation,amendments=Amendments, cancellation=Cancellation,reminder=Reminder,booking_today=Bookingtoday, purchasing_officer=purchasing_officer,reminder_status=reminder_status,booking_today_status=booking_today_status) messages.append('Job emails successfully updated') return HttpResponseRedirect('/admin/job_emails/')
排查出的问题及解决方法
1. 表单字段定义与Meta配置不匹配
问题:JobEmailDataForm的Meta.fields中包含is_email_customer和is_email_user两个字段,但表单类中未定义这两个字段,会导致表单初始化时抛出异常,模板渲染失败,表单无法正常交互。
解决:修改forms.py中的Meta.fields,移除未定义的字段,确保与表单类中实际定义的字段一致:
class Meta: widgets = { "confirmation": forms.TextInput, } fields = ['name', 'address', 'phone', 'website','email', 'replay_to_email' , 'logo','confirmation','amendments','cancellation','reminder','reminder_status', 'booking_today_status']
2. 表单未绑定初始化数据
问题:sendmail视图中创建JobEmailDataForm时未传入从数据库获取的company和email_data数据,表单为空且未填充用户已有信息,若存在必填项未填写,浏览器的HTML5验证会阻止表单提交。
解决:将获取到的用户数据作为initial参数传给表单,同时补充email_data中的字段:
def sendmail(request): client = request.user.client client_id = request.user.client.id email_data = JobEmailData.objects.filter(client_id=client).first() # 初始化表单数据 initial_data = {} # 填充client数据 client_data = Client.objects.get(id=client_id) initial_data['name'] = client_data.name initial_data['phone'] = client_data.phone initial_data['email'] = client_data.email initial_data['replay_to_email'] = client_data.replay_to_email or '' initial_data['website'] = client_data.website initial_data['address'] = get_client_address(client) # 填充email_data数据 if email_data: initial_data['reminder_status'] = email_data.reminder_status initial_data['booking_today_status'] = email_data.booking_today_status initial_data['purchasing_officer'] = email_data.purchasing_officer initial_data['confirmation'] = email_data.confirmation initial_data['amendments'] = email_data.amendments initial_data['cancellation'] = email_data.cancellation initial_data['booking_today'] = email_data.booking_today initial_data['reminder'] = email_data.reminder # 创建绑定初始数据的表单 form = JobEmailDataForm(initial=initial_data) bookedby = request.user return render(request, 'core/job_email_form_1.html', locals())
3. savejob视图逻辑错误
问题:doc.save()及后续处理逻辑位于if request.POST:块外部,当以GET请求访问/saveemaildata/时,doc变量未定义,会抛出NameError导致服务器500错误,POST请求提交后也可能因逻辑执行顺序问题报错。
解决:将所有处理逻辑移至if request.POST:块内部,确保只有POST请求时才执行数据保存操作:
def savejob(request): messages = [] client = request.user.client if request.POST: companyName = request.POST['name'] companyAddress = request.POST['address'] companyPhone = request.POST['phone'] companyWebsite = request.POST['website'] companyEmail = request.POST['email'] replay_to_email = request.POST['replay_to_email'] purchasing_officer = request.POST['purchasing_officer'] Confirmation = request.POST['confirmation'] Amendments = request.POST['amendments'] Cancellation = request.POST['cancellation'] Reminder = request.POST['reminder'] Bookingtoday = request.POST['booking_today'] reminder_status = request.POST['reminder_status'] booking_today_status = request.POST['booking_today_status'] client_id = ClientUser.objects.filter(id=request.user.id)[0].client_id doc = Client.objects.get(id=client_id) errors = [] if replay_to_email: if doc.replay_to_email != replay_to_email: doc.email_validated_status = 'Pending' send_email_validate.delay(request.user, 1) doc.name = companyName doc.replay_to_email = replay_to_email doc.email = companyEmail doc.phone = companyPhone doc.website = companyWebsite if 'logo' in request.FILES: doc.logo_url = request.FILES['logo'] doc.logo_url.storage = S3Boto3Storage(bucket_name='lightdegree') doc.save() objclient = Client.objects.get(id=client_id) clientid = objclient.id email_data = JobEmailData.objects.filter(client_id=clientid) if not email_data: objemail = JobEmailData(name=companyName, phone=companyPhone, website=companyWebsite, client_id=clientid, address=companyAddress, confirmation=Confirmation, amendments=Amendments, cancellation=Cancellation, reminder=Reminder, booking_today=Bookingtoday, purchasing_officer=purchasing_officer, reminder_status=reminder_status, booking_today_status=booking_today_status) objemail.save() else: JobEmailData.objects.filter(client_id=clientid).update(name=companyName, phone=companyPhone, website=companyWebsite, address=companyAddress, confirmation=Confirmation, amendments=Amendments, cancellation=Cancellation, reminder=Reminder, booking_today=Bookingtoday, purchasing_officer=purchasing_officer, reminder_status=reminder_status, booking_today_status=booking_today_status) messages.append('Job emails successfully updated') return HttpResponseRedirect('/admin/job_emails/') # 非POST请求返回表单页面或其他提示 return HttpResponseRedirect('/path/to/email/form/')
4. 表单action硬编码URL
问题:模板中表单action使用硬编码的/saveemaildata/,若URL配置变更会导致提交失败,且不符合Django最佳实践。
解决:使用Django的URL反向解析生成action地址:
<form method="post" enctype="multipart/form-data" action="{% url 'save_email_data' %}">
5. 前端验证或JS问题
排查:打开浏览器开发者工具(F12),查看控制台是否有JavaScript错误,检查表单字段是否有未填写的必填项(浏览器会在控制台提示或在字段旁显示错误)。
解决:修复JS错误,确保所有必填字段都有值,或根据需求调整表单字段的required属性。
内容的提问来源于stack exchange,提问作者Harika

