本文详解 Django 表单 is_valid() 始终返回 False 的核心原因——clean() 方法未显式返回 cleaned_data,并提供完整修复方案、HTML 模板优化建议及表单最佳实践。
本文详解 django 表单 `is_valid()` 始终返回 `false` 的核心原因——`clean()` 方法未显式返回 `cleaned_data`,并提供完整修复方案、html 模板优化建议及表单最佳实践。
在 Django 开发中,表单验证失败却难以定位原因,是新手常遇的典型问题。你遇到的 form.is_valid() 恒为 False,且日志中显示 telefone 和 foto 字段“必填”(即使已填写),其根本症结在于:自定义 clean() 方法未返回清洗后的数据。
Django 表单的 clean() 方法承担两项职责:执行跨字段验证 + 返回最终清洗结果。若仅调用 super().clean() 而忘记 return cleaned_data,Django 会将 cleaned_data 视为 None,导致所有字段(包括已提交的)被判定为“缺失”,从而触发默认的 required 错误。这是你看到 telefone 和 foto 报错的直接原因。
✅ 正确写法(关键修复):
# forms.py
from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
class cadastroForm(forms.Form):
nome = forms.CharField(label="Nome completo", required=True, max_length=100)
telefone = forms.CharField(label="Telefone", required=True, max_length=100)
email = forms.EmailField(label="Email", required=True)
iesb = forms.ChoiceField(
label="Frequenta o IESB",
choices=[("Yes", "Sim"), ("No", "Não")],
widget=forms.RadioSelect
)
mat = forms.IntegerField(label="Matrícula", required=False) # 注意:非必填,由逻辑控制
foto = forms.ImageField(label="Foto", required=False) # 文件字段需单独处理
def clean(self):
cleaned_data = super().clean() # 获取基础清洗结果
nome = cleaned_data.get("nome")
telefone = cleaned_data.get("telefone")
email = cleaned_data.get("email")
iesb = cleaned_data.get("iesb")
mat = cleaned_data.get("mat")
# 名字首字母大写(注意:Python 中是 title(),非 Title())
if nome and isinstance(nome, str):
cleaned_data["nome"] = nome.title()
# 电话号码校验
if telefone and re.search(r"[a-zA-Z]", telefone):
raise ValidationError(_("O número de telefone não pode possuir letras"))
# 邮箱格式校验(Django EmailField 已内置,此处为示例)
if email:
try:
validate_email(email)
except ValidationError:
raise ValidationError(_("Email inválido"))
# IESB 为 Yes 时,学号必填
if iesb == "Yes" and not mat:
raise ValidationError(_("Se frequenta o IESB, deve possuir uma matrícula"))
# ✅ 关键:必须显式返回清洗后的数据
return cleaned_data⚠️ 其他关键修正点:
-
HTML 表单 action 错误:
<form action="../../views.py"> 是严重错误!Django 表单必须提交到视图 URL(如 /cadastro/),而非 Python 文件路径。请确保 urls.py 中配置了对应路由,并在模板中使用 {% url 'cadastro-view' %}:<form method="POST" enctype="multipart/form-data"> {% csrf_token %} {{ form.as_p }} <!-- 推荐:自动渲染全部字段及错误 --> <button type="submit">Enviar</button> </form>? 必须添加 enctype="multipart/form-data" 才能上传文件(如 foto)!
-
视图需返回渲染响应:
你的 get_form 视图缺少 else 分支的 render() 调用,导致 GET 请求无响应:# views.py def get_form(request): if request.method == 'POST': form = cadastroForm(request.POST, request.FILES) # ✅ 关键:文件需传入 request.FILES if form.is_valid(): insert_data( form.cleaned_data['nome'], form.cleaned_data['mat'], form.cleaned_data['telefone'], form.cleaned_data['email'] ) return HttpResponseRedirect(reverse('all-borrowed')) else: form = cadastroForm() # ✅ 必须返回渲染结果,传递表单实例 return render(request, 'seu_template.html', {'form': form}) -
模型与文件处理建议:
- foto 字段应关联 ImageField 到 Django 模型,并在 insert_data() 中使用 form.save() 或手动处理 request.FILES['foto'];
- 避免在视图中硬编码业务逻辑(如 insert_data()),推荐使用 Model Forms 或 Django 的 save() 方法。
? 总结:Django 表单验证的核心原则是——clean() 必须返回 cleaned_data;文件上传必传 request.FILES;模板 action 必须指向有效 URL 并设置 enctype。遵循此模式,90% 的“表单不生效”问题将迎刃而解。


















