Django 表单
以下是用中文详细讲解 Django 表单(Forms)的指南,涵盖表单的作用、创建方法、常用功能、验证、渲染以及最佳实践。我会保持简洁清晰,适合初学者和进阶用户,并提供实用示例和代码。你提到需要生成 artifact,因此我会将关键代码包裹在 ““` 标签中。如果你有特定问题或需要更深入的内容,请随时告知!
什么是 Django 表单?
Django 表单是用于处理用户输入的工具,负责数据收集、验证和处理。表单与 HTML 表单元素结合,简化了前端渲染和后端验证流程。
- 作用:
- 生成 HTML 表单元素。
- 验证用户输入(如必填、格式正确性)。
- 处理提交数据(保存到数据库或执行其他操作)。
- 特点:
- 支持自动生成表单字段(基于模型或手动定义)。
- 内置验证机制,防止无效数据。
- 与模板集成,灵活渲染。
1. 创建表单
Django 提供两种主要表单类型:普通表单(forms.Form
)和模型表单(forms.ModelForm
)。
1.1 普通表单(forms.Form
)
用于手动定义字段,适合非模型相关的数据收集。
示例:myapp/forms.py
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100, label='姓名')
email = forms.EmailField(label='邮箱')
message = forms.CharField(widget=forms.Textarea, label='留言')
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100, label=’姓名’)
email = forms.EmailField(label=’邮箱’)
message = forms.CharField(widget=forms.Textarea, label=’留言’)
“`python
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100, label=’姓名’)
email = forms.EmailField(label=’邮箱’)
message = forms.CharField(widget=forms.Textarea, label=’留言’)
- **说明**:
- 继承 `forms.Form`。
- 字段类型(如 `CharField`、`EmailField`)定义输入类型。
- `label`:字段显示名称。
- `widget`:自定义 HTML 输入控件(如 `Textarea`)。
#### 1.2 模型表单(`forms.ModelForm`)
基于模型自动生成表单字段,适合与数据库交互。
##### 示例:`myapp/forms.py`
python
from django import forms
from .models import Item
class ItemForm(forms.ModelForm):
class Meta:
model = Item
fields = [‘name’, ‘price’, ‘category’, ‘description’]
labels = {
‘name’: ‘商品名称’,
‘price’: ‘价格’,
‘category’: ‘分类’,
‘description’: ‘描述’,
}
widgets = {
‘description’: forms.Textarea(attrs={‘rows’: 4}),
}
- **说明**:
- 继承 `forms.ModelForm`。
- `Meta` 类指定关联模型(`model`)、包含字段(`fields`)、标签(`labels`)和控件(`widgets`)。
- 自动根据模型字段生成表单字段。
#### 模型定义(参考)
假设 `myapp/models.py`:
python
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Item(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.name
---
### 2. 使用表单
表单通常在视图中处理,结合模板渲染。
#### 2.1 视图处理
##### 示例:`myapp/views.py`
python
from django.shortcuts import render, redirect
from .forms import ContactForm, ItemForm
from .models import Item
def contact(request):
if request.method == ‘POST’:
form = ContactForm(request.POST)
if form.is_valid():
# 处理表单数据(例如发送邮件或保存)
name = form.cleaned_data[‘name’]
email = form.cleaned_data[’email’]
message = form.cleaned_data[‘message’]
# 示例:打印数据
print(f”收到留言:{name} ({email}): {message}”)
return redirect(‘success’)
else:
form = ContactForm()
return render(request, ‘myapp/contact.html’, {‘form’: form})
def add_item(request):
if request.method == ‘POST’:
form = ItemForm(request.POST)
if form.is_valid():
form.save() # 直接保存到数据库
return redirect(‘item_list’)
else:
form = ItemForm()
return render(request, ‘myapp/add_item.html’, {‘form’: form})
def item_list(request):
items = Item.objects.all()
return render(request, ‘myapp/item_list.html’, {‘items’: items})
- **说明**:
- `request.method == 'POST'`:处理表单提交。
- `form.is_valid()`:验证表单数据。
- `form.cleaned_data`:获取验证后的数据。
- `form.save()`:对于 `ModelForm`,直接保存到数据库。
- `redirect`:提交成功后重定向到其他页面。
#### 2.2 模板渲染
##### 示例:`myapp/templates/myapp/contact.html`
html
{% extends ‘base.html’ %}
{% block content %}
联系我们
{% csrf_token %} {{ form.as_p }} 提交
{% endblock %}
##### 示例:`myapp/templates/myapp/add_item.html`
html
{% extends ‘base.html’ %}
{% block content %}
添加商品
{% csrf_token %}
{{ form.name.label_tag }} {{ form.name }} {% if form.name.errors %}
{{ form.name.errors }} {% endif %}
{{ form.price.label_tag }} {{ form.price }} {% if form.price.errors %}
{{ form.price.errors }} {% endif %}
{{ form.category.label_tag }} {{ form.category }} {% if form.category.errors %}
{{ form.category.errors }} {% endif %}
{{ form.description.label_tag }} {{ form.description }} {% if form.description.errors %}
{{ form.description.errors }} {% endif %} 保存
{% endblock %}
##### 示例:`myapp/templates/myapp/item_list.html`
html
{% extends ‘base.html’ %}
{% block content %}
商品列表
- {{ item.name }} – ¥{{ item.price }} ({{ item.category.name }})
- 暂无商品
{% endblock %}
- **说明**:
- `{% csrf_token %}`:防止跨站请求伪造(CSRF)攻击,必填。
- `form.as_p`:将表单渲染为 `<p>` 标签包裹的字段(其他选项:`as_table`、`as_ul`)。
- 手动渲染(如 `{{ form.name }}`):更灵活,可自定义样式。
- `form.errors`:显示验证错误。
#### 2.3 URL 配置
##### 示例:`myapp/urls.py`
python
from django.urls import path
from . import views
urlpatterns = [
path(‘contact/’, views.contact, name=’contact’),
path(‘add-item/’, views.add_item, name=’add_item’),
path(‘items/’, views.item_list, name=’item_list’),
path(‘success/’, lambda request: render(request, ‘myapp/success.html’), name=’success’),
]
##### 项目 `urls.py`
python
from django.urls import path, include
urlpatterns = [
path(”, include(‘myapp.urls’)),
]
#### 2.4 成功页面
##### 示例:`myapp/templates/myapp/success.html`
html
{% extends ‘base.html’ %}
{% block content %}
提交成功
感谢您的留言!
返回
{% endblock %}
---
### 3. 表单验证
Django 提供内置和自定义验证机制。
#### 3.1 内置验证
- 字段自带验证(如 `EmailField` 确保格式正确)。
- 示例:`max_length` 限制字符长度,`required=True` 确保必填。
#### 3.2 自定义验证
在表单类中定义 `clean_<field>` 或 `clean` 方法。
##### 示例:`myapp/forms.py`
python
from django import forms
from .models import Item
class ItemForm(forms.ModelForm):
class Meta:
model = Item
fields = [‘name’, ‘price’, ‘category’, ‘description’]
labels = {
‘name’: ‘商品名称’,
‘price’: ‘价格’,
‘category’: ‘分类’,
‘description’: ‘描述’,
}
def clean_price(self):
price = self.cleaned_data['price']
if price <= 0:
raise forms.ValidationError('价格必须大于 0')
return price
def clean(self):
cleaned_data = super().clean()
name = cleaned_data.get('name')
description = cleaned_data.get('description')
if name and description and name.lower() in description.lower():
raise forms.ValidationError('描述中不能包含商品名称')
return cleaned_data
- **说明**:
- `clean_price`:验证单个字段。
- `clean`:验证多个字段的组合逻辑。
- `ValidationError`:抛出错误,显示在模板中。
---
### 4. 高级功能
#### 4.1 动态字段
根据上下文调整表单字段。
python
class ItemForm(forms.ModelForm):
def init(self, *args, *kwargs): super().init(args, **kwargs)
self.fields[‘category’].queryset = Category.objects.filter(is_active=True)
#### 4.2 文件上传
使用 `FileField` 或 `ImageField`。
python
class UploadForm(forms.Form):
file = forms.FileField()
在模板中:
html {% csrf_token %} {{ form.as_p }} 上传
- **注意**:表单需设置 `enctype="multipart/form-data"`。
#### 4.3 表单集(Formsets)
处理多个相同表单。
python
from django.forms import formset_factory
ItemFormSet = formset_factory(ItemForm, extra=2) # 创建 2 个空表单
在视图中:
python
def manage_items(request):
if request.method == ‘POST’:
formset = ItemFormSet(request.POST)
if formset.is_valid():
for form in formset:
if form.cleaned_data:
form.save()
return redirect(‘item_list’)
else:
formset = ItemFormSet()
return render(request, ‘myapp/manage_items.html’, {‘formset’: formset})
---
### 5. 最佳实践
1. **分离逻辑**:
- 表单负责验证,视图负责业务逻辑。
- 使用 `ModelForm` 简化数据库操作。
2. **自定义样式**:
- 手动渲染字段,结合 CSS 框架(如 Bootstrap):
```html
<div class="form-group">
{{ form.name.label_tag }}
{{ form.name }}
{% if form.name.errors %}
<div class="alert alert-danger">{{ form.name.errors }}</div>
{% endif %}
</div>
```
3. **安全注意**:
- 始终包含 `{% csrf_token %}`。
- 避免直接保存用户输入,使用 `form.cleaned_data`。
4. **复用表单**:
- 将通用表单提取到单独文件(如 `forms/common.py`)。
- 使用继承扩展表单功能。
5. **性能优化**:
- 限制查询集(如 `category.queryset`)。
- 避免在表单初始化中执行复杂逻辑。
---
### 6. 常见问题
1. **表单未显示**:
- 检查模板中是否正确传递 `form` 变量。
- 确认 `urls.py` 和视图配置无误。
2. **验证错误不显示**:
- 确保模板渲染了 `form.errors` 或 `form.field.errors`。
- 检查视图中是否调用 `form.is_valid()`。
3. **CSRF 错误**:
- 确认 `<form>` 包含 `{% csrf_token %}`。
- 检查浏览器是否禁用了 cookies。
4. **文件上传失败**:
- 确保 `enctype="multipart/form-data"`。
- 检查 `settings.py` 中的 `MEDIA_URL` 和 `MEDIA_ROOT`。
---
### 7. 运行项目
1. 确保模型迁移:
bash
python manage.py makemigrations
python manage.py migrate
2. 启动服务器:
bash
python manage.py runserver
“`
- 访问:
- 联系表单:
http://127.0.0.1:8000/contact/
- 添加商品:
http://127.0.0.1:8000/add-item/
- 商品列表:
http://127.0.0.1:8000/items/
总结
Django 表单通过 Form
和 ModelForm
提供强大的输入处理能力,结合视图和模板,实现数据收集、验证和渲染。正确配置表单、利用验证机制、遵循最佳实践,可以快速构建安全、用户友好的 Web 应用。
如果你需要更复杂的表单示例(如动态表单集、AJAX 提交)、特定功能的深入讲解,或调试表单问题的方法,请告诉我!