Django表单渲染与处理详解
登录页面示例:GET与POST请求
以一个登录页面为例,我们可以看到两种常见的HTTP请求方式在表单处理中的应用。
GET方式:用于页面加载
当用户首次访问登录页面时,通常使用GET请求来获取页面内容。在这种情况下,表单的渲染不一定需要form组件,但表单字段的name属性应与后续POST请求中用于解析数据的键名保持一致。
POST方式:用于数据提交
当用户填写完表单并点击提交按钮时,使用POST请求将用户输入的数据发送到服务器。服务器端接收这些数据,进行验证和处理。
视图函数(views.py)
from django.shortcuts import render, HttpResponse, redirect
from app01.models import UserInfo # 假设UserInfo模型已定义
def handle_login(request):
if request.method == "POST":
user_name = request.POST.get('username')
pass_word = request.POST.get('password')
# 模拟用户验证
user_query = UserInfo.objects.filter(username=user_name, password=pass_word)
if user_query:
# 登录成功,设置session并重定向
request.session['logged_in_user'] = user_name
return HttpResponse('欢迎登录系统!')
else:
return HttpResponse('用户名或密码错误!')
# GET请求,渲染登录页面
return render(request, 'login.html', locals())
登录页面模板(login.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.js"></script>
<style>
form { margin-top: 100px; }
.btn { display: block; margin-left: auto; margin-right: auto; }
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<form action="/login/" method="post" class="center-block">
{% csrf_token %}
<div class="form-group">
<label for="username_input">用户名:</label>
<input type="text" class="form-control" id="username_input" name="username" required>
</div>
<div class="form-group">
<label for="password_input">密码:</label>
<input type="password" class="form-control" id="password_input" name="password" required>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="登录">
</div>
</form>
</div>
</div>
</div>
</body>
</html>
使用Django Forms进行表单渲染与验证
Django的Forms库提供了更强大、更规范的方式来创建、渲染和验证表单。
自定义表单类
我们可以定义一个继承自django.forms.Form的类,来声明表单字段及其属性。
视图函数(views.py)
from django.shortcuts import render, HttpResponse
from django import forms
from django.forms import widgets
from app01.models import UserInfo, Sex, Account # 假设模型已定义
class RegistrationForm(forms.Form):
user_name = forms.CharField(
max_length=12,
min_length=6,
label='用户名',
widget=widgets.TextInput(attrs={'class': "form-control", 'id': 'user_name_field'})
)
pass_word = forms.CharField(
min_length=6,
max_length=12,
label='密码',
widget=widgets.PasswordInput(attrs={'class': 'form-control', 'id': 'password_field'})
)
gender_choice = forms.ChoiceField(
choices=Sex.objects.all().values_list('id', 'sex'), # 从数据库动态获取选项
label="性别",
initial=lambda: Sex.objects.get(sex="保密").id if Sex.objects.filter(sex="保密").exists() else 1, # 设置默认值
widget=forms.widgets.RadioSelect
)
def register(request):
error_message = ""
form_instance = RegistrationForm()
if request.method == "POST":
form_instance = RegistrationForm(request.POST)
if form_instance.is_valid():
# cleaned_data 包含了经过验证的、清洗过的数据
input_username = form_instance.cleaned_data.get("user_name")
input_password = form_instance.cleaned_data.get("pass_word")
selected_gender_id = form_instance.cleaned_data.get('gender_choice')
# 创建新用户记录
Account.objects.create(name=input_username, pwd=input_password, Tosex_id=selected_gender_id)
return HttpResponse('注册成功!')
else:
# 表单验证失败,可以在这里处理错误信息
error_message = "表单验证失败,请检查输入。"
return render(request, "registration.html", {"form_obj": form_instance, "error_msg": error_message})
注册页面模板(registration.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
<title>用户注册</title>
<style>
.error-text { color: red; }
#form-container {
height: 300px;
position: absolute;
margin-top: -150px;
top: 50%;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3" id="form-container">
<form action="" method="post" novalidate>
{% csrf_token %}
<!-- 用户名字段渲染 -->
<div class="form-group">
<label for="{{ form_obj.user_name.id_for_label }}" class="control-label">{{ form_obj.user_name.label }}</label>
<div>
{{ form_obj.user_name }}
{% if form_obj.user_name.errors %}
<span class="error-text">{{ form_obj.user_name.errors.0 }}</span>
{% endif %}
</div>
</div>
<!-- 密码字段渲染 -->
<div class="form-group">
<label for="{{ form_obj.pass_word.id_for_label }}" class="control-label">{{ form_obj.pass_word.label }}</label>
<div>
{{ form_obj.pass_word }}
{% if form_obj.pass_word.errors %}
<span class="error-text">{{ form_obj.pass_word.errors.0 }}</span>
{% endif %}
</div>
</div>
<!-- 性别选择字段渲染 -->
<div class="form-group">
<label class="control-label">{{ form_obj.gender_choice.label }}</label>
<div>
{{ form_obj.gender_choice }}
{% if form_obj.gender_choice.errors %}
<span class="error-text">{{ form_obj.gender_choice.errors.0 }}</span>
{% endif %}
</div>
</div>
<!-- 提交按钮与全局错误信息 -->
<div class="form-group">
<input type="submit" class="btn btn-primary" value="注册">
{% if error_msg %}
<span class="error-text">{{ error_msg }}</span>
{% endif %}
</div>
</form>
</div>
</div>
</div>
<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.js"></script>
</body>
</html>
使用ModelForm简化模型表单
当表单字段与数据库模型一一对应时,可以使用ModelForm来自动生成表单,大大减少代码量。
ModelForm定义
通过在内部类Meta中指定model和fields,可以快速创建一个基于模型的表单。
视图函数(views.py)
from django.shortcuts import render, HttpResponse, redirect
from django.forms import ModelForm
from django.forms import widgets as form_widgets
from app01.models import Book, UserInfo # 假设Book和UserInfo模型已定义
class BookForm(ModelForm):
class Meta:
model = Book
fields = '__all__' # 或者指定 ['title', 'price', 'publisher', 'authors']
widgets = {
'title': form_widgets.TextInput(attrs={'class': 'form-control'}),
'price': form_widgets.NumberInput(attrs={'class': 'form-control'}), # 使用NumberInput更合适
'publisher': form_widgets.Select(attrs={'class': 'form-control'}),
'authors': form_widgets.SelectMultiple(attrs={'class': 'form-control'})
}
# 可以自定义字段标签
labels = {
'title': '书名',
'price': '价格',
'publisher': '出版社',
'authors': '作者'
}
class LoginForm(ModelForm):
class Meta:
model = UserInfo
fields = ['username', 'password'] # 只包含需要的字段
widgets = {
'username': form_widgets.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入用户名'}),
'password': form_widgets.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入密码'})
}
labels = {
'username': '用户名',
'password': '密码'
}
# 假设 book_list, add, delete, edit 是处理图书相关操作的视图函数
# ... (省略book_list, add, delete, edit函数的详细实现) ...
def login_view(request):
if request.method == "POST":
user_input_form = LoginForm(request.POST)
if user_input_form.is_valid():
user_name = user_input_form.cleaned_data.get('username')
pass_word = user_input_form.cleaned_data.get('password')
# 验证用户
user_exists = UserInfo.objects.filter(username=user_name, password=pass_word).first()
if user_exists:
request.session['current_user'] = user_name
return redirect('/book_list/') # 重定向到图书列表页
else:
# 登录失败,可以在form_login中添加错误信息
pass
else:
user_input_form = LoginForm() # GET请求,创建空表单
return render(request, 'login_form_page.html', {'login_form_obj': user_input_form})
def add_book_view(request):
if request.method == 'POST':
book_creation_form = BookForm(request.POST)
if book_creation_form.is_valid():
book_creation_form.save() # 直接保存到数据库
return redirect('/book_list/')
else:
book_creation_form = BookForm()
return render(request, 'add_book_page.html', {'book_form_instance': book_creation_form})
# ... 其他视图函数 ...
登录页面模板(login_form_page.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>ModelForm 登录</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
<style>
form { margin-top: 100px; }
</style>
</head>
<body>
<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.js"></script>
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<form action="" method="post">
{% csrf_token %}
<div class="form-group">
<label for="{{ login_form_obj.username.id_for_label }}">{{ login_form_obj.username.label }}</label>
{{ login_form_obj.username }}
{% if login_form_obj.username.errors %}<span class="error-text">{{ login_form_obj.username.errors.0 }}</span>{% endif %}
</div>
<div class="form-group">
<label for="{{ login_form_obj.password.id_for_label }}">{{ login_form_obj.password.label }}</label>
{{ login_form_obj.password }}
{% if login_form_obj.password.errors %}<span class="error-text">{{ login_form_obj.password.errors.0 }}</span>{% endif %}
</div>
<input type="submit" class="btn btn-primary btn-block" value="登录">
</form>
</div>
</div>
</div>
</body>
</html>
添加图书页面模板(add_book_page.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>添加图书</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
<style>
form { margin-top: 50px; }
</style>
</head>
<body>
<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.js"></script>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h2 class="text-center">添加新图书</h2>
<form action="" method="post" enctype="multipart/form-data"> <!-- 如果有文件上传,需要 enctype -->
{% csrf_token %}
{% for field in book_form_instance %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
{% if field.errors %}
<span class="error-text">{{ field.errors.0 }}</span>
{% endif %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
<input type="submit" class="btn btn-success btn-block" value="保存图书">
</form>
</div>
</div>
</div>
</body>
</html>