第8章:Django表单(Forms)
8.2 ModelForm
ModelForm创建
ModelForm是Django提供的一个强大的表单类,它可以根据模型自动生成表单字段。这大大减少了重复代码,特别是在创建与模型字段对应的表单时。
基本的ModelForm定义:
python
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['title', 'content', 'category', 'tags']完整的ModelForm示例:
python
# models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
def __str__(self):
return self.name
class Tag(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
tags = models.ManyToManyField(Tag, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_published = models.BooleanField(default=False)
def __str__(self):
return self.title
# forms.py
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['title', 'content', 'category', 'tags', 'is_published']
widgets = {
'title': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': '请输入文章标题'
}),
'content': forms.Textarea(attrs={
'class': 'form-control',
'rows': 10,
'placeholder': '请输入文章内容'
}),
'category': forms.Select(attrs={'class': 'form-control'}),
'tags': forms.CheckboxSelectMultiple(),
'is_published': forms.CheckboxInput(attrs={'class': 'form-check-input'})
}
labels = {
'title': '文章标题',
'content': '文章内容',
'category': '分类',
'tags': '标签',
'is_published': '是否发布'
}
help_texts = {
'title': '请输入一个吸引人的标题',
'tags': '可选择多个标签'
}字段自定义
ModelForm允许你自定义字段,包括字段类型、验证规则和小部件:
python
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
# 添加额外字段
summary = forms.CharField(
max_length=300,
widget=forms.Textarea(attrs={'rows': 3}),
required=False,
label='文章摘要'
)
class Meta:
model = Article
fields = ['title', 'summary', 'content', 'category', 'tags', 'is_published']
# 排除某些字段
# exclude = ['author', 'created_at', 'updated_at']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 动态修改字段属性
self.fields['title'].widget.attrs.update({
'class': 'form-control',
'placeholder': '请输入文章标题'
})
# 根据条件过滤选择项
if 'category' in self.fields:
self.fields['category'].queryset = Category.objects.filter(is_active=True)
# 自定义字段验证
def clean_title(self):
title = self.cleaned_data['title']
if len(title) < 5:
raise forms.ValidationError("标题至少需要5个字符")
return title
# 重写save方法
def save(self, commit=True):
article = super().save(commit=False)
# 在保存前进行额外处理
if not article.slug:
article.slug = slugify(article.title)
if commit:
article.save()
self.save_m2m() # 保存多对多关系
return article保存数据
ModelForm提供了简单的方式来保存表单数据到数据库:
python
# views.py
from django.shortcuts import render, redirect, get_object_or_404
from .forms import ArticleForm
from .models import Article
def create_article(request):
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
# 方法1:直接保存
article = form.save()
# 方法2:保存前设置额外字段
article = form.save(commit=False)
article.author = request.user
article.save()
form.save_m2m() # 保存多对多关系
return redirect('article_detail', pk=article.pk)
else:
form = ArticleForm()
return render(request, 'articles/create.html', {'form': form})
def edit_article(request, pk):
article = get_object_or_404(Article, pk=pk)
if request.method == 'POST':
form = ArticleForm(request.POST, instance=article)
if form.is_valid():
article = form.save()
return redirect('article_detail', pk=article.pk)
else:
form = ArticleForm(instance=article)
return render(request, 'articles/edit.html', {'form': form, 'article': article})联系表单示例
以下是一个完整的联系表单示例:
python
# models.py
from django.db import models
class ContactMessage(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
subject = models.CharField(max_length=200)
message = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
is_read = models.BooleanField(default=False)
def __str__(self):
return f"{self.name} - {self.subject}"
# forms.py
from django import forms
from .models import ContactMessage
class ContactForm(forms.ModelForm):
class Meta:
model = ContactMessage
fields = ['name', 'email', 'subject', 'message']
widgets = {
'name': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': '请输入您的姓名'
}),
'email': forms.EmailInput(attrs={
'class': 'form-control',
'placeholder': '请输入您的邮箱'
}),
'subject': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': '请输入主题'
}),
'message': forms.Textarea(attrs={
'class': 'form-control',
'rows': 5,
'placeholder': '请输入您的消息'
})
}
labels = {
'name': '姓名',
'email': '邮箱',
'subject': '主题',
'message': '消息内容'
}
# views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import ContactForm
def contact_view(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, '您的消息已发送成功!')
return redirect('contact')
else:
form = ContactForm()
return render(request, 'contact.html', {'form': form})
<!-- contact.html -->
<div class="container">
<h2>联系我们</h2>
{% if messages %}
{% for message in messages %}
<div class="alert alert-success">{{ message }}</div>
{% endfor %}
{% endif %}
<form method="post">
{% csrf_token %}
<div class="mb-3">
{{ form.name.label_tag }}
{{ form.name }}
{% if form.name.errors %}
<div class="text-danger">{{ form.name.errors }}</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.email.label_tag }}
{{ form.email }}
{% if form.email.errors %}
<div class="text-danger">{{ form.email.errors }}</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.subject.label_tag }}
{{ form.subject }}
{% if form.subject.errors %}
<div class="text-danger">{{ form.subject.errors }}</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.message.label_tag }}
{{ form.message }}
{% if form.message.errors %}
<div class="text-danger">{{ form.message.errors }}</div>
{% endif %}
</div>
<button type="submit" class="btn btn-primary">发送消息</button>
</form>
</div>ModelForm大大简化了表单处理过程,特别是在与数据库模型交互时,提供了自动化的字段映射和验证功能。
小结
ModelForm是Django表单系统的重要组成部分:
- ✅ 根据模型自动生成表单字段
- ✅ 支持字段自定义和验证
- ✅ 简化数据保存到数据库的过程
- ✅ 提供灵活的字段控制(fields/exclude)
- ✅ 支持小部件和标签的自定义
掌握ModelForm能够显著提高开发效率,减少重复代码。
下一篇
我们将学习表单集和高级表单特性。