Django表单类
Django 的表单类(Form 和 ModelForm)是处理用户输入数据的核心组件,它们提供了数据验证、清洗、HTML 渲染和错误提示等功能。
一、基础概念
1. 表单类的两种类型
Form:普通表单类,手动定义字段和验证逻辑。ModelForm:模型表单类,自动映射模型字段,简化数据库操作。
2. 核心功能
- 数据验证:检查输入是否符合规则(如必填项、格式、唯一性)。
- 数据清洗:转换数据类型(如字符串转整数)、去除无效字符。
- 错误处理:自动生成友好的错误信息。
- HTML 渲染:快速生成表单 HTML(如
{{ form.as_p }})。
二、普通表单类(Form)
1. 定义表单类
# forms.py
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(label='姓名', max_length=100)
email = forms.EmailField(label='邮箱')
message = forms.CharField(label='留言', widget=forms.Textarea)
2. 字段类型
常用字段类型:
CharField:文本字段(默认单行)EmailField:邮箱字段(自动验证格式)IntegerField:整数字段BooleanField:布尔字段(复选框)DateField:日期字段(格式:YYYY-MM-DD)ChoiceField:下拉选择框FileField:文件上传字段
3. 字段参数
常用参数:
label:显示标签required:是否必填(默认True)max_length/min_length:最大 / 最小长度widget:自定义 HTML 控件(如Textarea、PasswordInput)initial:初始值help_text:帮助文本error_messages:自定义错误信息
4. 在视图中使用
# views.py
from django.shortcuts import render
from .forms import ContactForm
def contact_view(request):
# 处理表单提交的视图函数
if request.method == 'POST':
# 当用户提交表单时,创建绑定了POST数据的表单实例
form = ContactForm(request.POST)
# 验证表单数据
if form.is_valid():
# 从验证后的cleaned_data中获取字段值
name = form.cleaned_data['name']
email = form.cleaned_data['email']
message = form.cleaned_data['message']
# TODO: 处理表单数据的逻辑
# 这里可以添加发送邮件、保存到数据库等操作
# 例如: send_email(name, email, message)
# 成功处理后重定向到成功页面
return render(request, 'success.html')
else:
# GET请求(首次访问页面)时创建未绑定数据的空表单
form = ContactForm() # GET请求返回空表单
# 渲染包含表单的模板,无论表单是否有效都会执行到这里
return render(request, 'contact.html', {'form': form})
5. 在模板中渲染
<!-- contact.html -->
<form method="post">
{% csrf_token %}
{{ form.as_p }} <!-- 快速渲染为 <p> 标签包裹的表单 -->
<button type="submit">提交</button>
</form>
更精细的控制:
<form method="post">
{% csrf_token %}
<div>
<label for="{{ form.name.id_for_label }}">{{ form.name.label }}</label>
{{ form.name }}
{% if form.name.errors %}
<div class="error">{{ form.name.errors.as_text }}</div>
{% endif %}
</div>
<!-- 其他字段类似 -->
<button type="submit">提交</button>
</form>
三、模型表单类(ModelForm)
1. 定义模型表单
# models.py
from django.db import models
class Comment(models.Model):
content = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
# forms.py
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content'] # 只让用户填写 content 字段
# 或排除某些字段: exclude = ['user', 'created_at']
2. 自动映射字段
ModelForm 会自动根据模型字段类型创建表单字段:
CharField→models.CharFieldIntegerField→models.IntegerFieldBooleanField→models.BooleanFieldForeignKey→ModelChoiceField(下拉选择框)
3. 在视图中使用
# views.py
from django.shortcuts import render, redirect
from .forms import CommentForm
def add_comment(request):
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False) # 创建对象但暂不保存
comment.user = request.user # 设置关联用户
comment.save() # 保存到数据库
return redirect('success')
else:
form = CommentForm()
return render(request, 'add_comment.html', {'form': form})
四、表单验证
1. 字段级验证
在表单类中定义 clean_<field_name>() 方法:
class ContactForm(forms.Form):
email = forms.EmailField()
def clean_email(self):
email = self.cleaned_data['email']
if "spam" in email:
raise forms.ValidationError("邮箱地址包含禁用关键词")
return email
2. 表单级验证
定义 clean() 方法验证多个字段:
class DateRangeForm(forms.Form):
start_date = forms.DateField()
end_date = forms.DateField()
def clean(self):
cleaned_data = super().clean()
start_date = cleaned_data.get("start_date")
end_date = cleaned_data.get("end_date")
if start_date and end_date and start_date > end_date:
raise forms.ValidationError("开始日期不能晚于结束日期")
return cleaned_data
3. 自定义验证器
可重用的验证逻辑:
# validators.py
from django.core.exceptions import ValidationError
def validate_even(value):
if value % 2 != 0:
raise ValidationError(f"{value} 不是偶数")
# forms.py
from .validators import validate_even
class MyForm(forms.Form):
number = forms.IntegerField(validators=[validate_even])
五、高级用法
1. 动态修改表单字段
class MyForm(forms.Form):
choice = forms.ChoiceField(choices=[])
def __init__(self, *args, **kwargs):
extra_choices = kwargs.pop('extra_choices', [])
super().__init__(*args, **kwargs)
self.fields['choice'].choices = [('default', '默认选项')] + list(extra_choices)
2. 文件上传
# forms.py
class UploadForm(forms.Form):
file = forms.FileField()
# views.py
def upload_view(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES) # 注意 request.FILES
if form.is_valid():
file = form.cleaned_data['file']
# 处理文件...
else:
form = UploadForm()
return render(request, 'upload.html', {'form': form})
3. 表单集(Formsets)
处理多个相同表单:
# forms.py
from django.forms import formset_factory
class ArticleForm(forms.Form):
title = forms.CharField()
pub_date = forms.DateField()
ArticleFormSet = formset_factory(ArticleForm, extra=2) # 默认创建2个空表单
# views.py
def manage_articles(request):
formset = ArticleFormSet(request.POST or None)
if formset.is_valid():
for form in formset:
if form.cleaned_data:
# 处理每个表单数据
pass
return render(request, 'manage_articles.html', {'formset': formset})
六、模板渲染技巧
1. 快速渲染
{{ form.as_p }} <!-- <p> 标签包裹 -->
{{ form.as_table }} <!-- <tr> 标签包裹 -->
{{ form.as_ul }} <!-- <li> 标签包裹 -->
2. 自定义渲染
<form method="post">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
{{ field.label_tag }}
{{ field }}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
{% if field.errors %}
<div class="alert alert-danger">{{ field.errors.as_text }}</div>
{% endif %}
</div>
{% endfor %}
<button type="submit">提交</button>
</form>
七、与 ModelForm 相关的坑点
-
commit=False的局限性: -
不能用于多对多字段(需先
save(),再添加关联)。
python
post = form.save(commit=False)
post.save() # 必须先保存,才能添加多对多关系
post.tags.set(tags) # 添加多对多关系
-
字段权限控制:
-
不要在
fields中包含敏感字段(如用户密码),而是在视图中手动设置。 -
自动填充字段:
-
用
initial参数预填数据:
python
form = CommentForm(initial={'content': '默认内容'})
八、最佳实践
-
表单类放在
forms.py:保持代码组织清晰。 -
重用验证逻辑:用自定义验证器减少重复代码。
-
使用 ModelForm:能自动映射模型字段,减少样板代码。
发表评论