Skip to content

Django - 表单处理

处理 HTML 表单是 Web 开发的基本组成部分。Django 提供了一个强大的表单库(django.forms),用于处理将表单渲染为 HTML、验证提交的数据,并将这些数据转换为 Python 类型。

在 Django 中创建表单类似于创建模型(Model)。你定义一个继承自 forms.Form(如果表单直接映射到模型,则继承自 forms.ModelForm)的类。类的属性代表表单字段(form fields)。

我们来创建一个简单的登录表单。在你的 myapp 文件夹中创建一个 forms.py 文件:

myapp/forms.py
from django import forms
class LoginForm(forms.Form):
username = forms.CharField(max_length=100, required=True)
password = forms.CharField(widget=forms.PasswordInput(), required=True)

这里:

  • LoginForm 继承自 forms.Form。
  • username 是一个 CharField(字符字段),代表一个文本输入。
  • password 也是一个 CharField,但我们使用 PasswordInput widget(小部件)来将其渲染为 HTML 中的 <input type=“password”>,以隐藏输入内容。
  • max_length 提供了基本验证。required=True 表示该字段不能为空。
  • Django 提供了许多字段类型(如 EmailField、IntegerField、BooleanField 等)和 widget(如 Textarea、CheckboxInput、Select 等)。请参阅 Django 表单字段 和 widget 文档。

视图(View)处理显示和处理表单的逻辑。常见的模式是在同一个视图中处理 GET(显示空表单)和 POST(处理提交的数据)请求。

我们在 myapp/views.py 中创建一个 login_view:

myapp/views.py
from django.shortcuts import render, redirect
from .forms import LoginForm
# Assuming you have some authentication logic
# 假设你有一些认证逻辑
# from django.contrib.auth import authenticate, login as auth_login
def login_view(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
#
# *** Add authentication logic here! ***
# *** 在此处添加认证逻辑! ***
# Example (requires django.contrib.auth setup):
# 示例(需要设置 django.contrib.auth):
# user = authenticate(request, username=username, password=password)
# if user is not None:
# auth_login(request, user)
# return redirect('home') # Redirect to a success page
# return redirect('home') # 重定向到成功页面
# else:
# # Handle invalid login (e.g., add form error)
# # 处理无效登录(例如,添加表单错误)
# form.add_error(None, 'Invalid username or password.')
#
# For this simple example, we'll just render a success page
# 对于这个简单的示例,我们只渲染一个成功页面
return render(request, 'myapp/loggedin.html', {'username': username})
# If form is invalid, render the page with the form and errors
# 如果表单无效,使用包含表单和错误的页面重新渲染
else: # GET request
# GET 请求
form = LoginForm()
return render(request, 'myapp/login.html', {'form': form})

解释:

  • 如果 request.method 是 ‘POST’,我们使用提交的数据(request.POST)实例化表单。
  • form.is_valid() 触发验证。它检查字段要求(如 required=True)并运行任何自定义验证方法。
  • 如果有效,数据将在 form.cleaned_data 中可用,这是一个包含已验证 Python 对象的字典。
  • 重要: 上述示例缺少真实的认证。在实际应用中,你会在此时使用 django.contrib.auth.authenticate 和 login。
  • 如果表单无效,或者如果是 GET 请求,我们会实例化一个空表单(LoginForm()),或者重新渲染已绑定数据的表单(该表单现在包含错误消息)。
  • 我们将 form 对象传递给模板上下文(template context)。

我们需要两个模板:一个用于显示表单(login.html),一个用于成功消息(loggedin.html)。

myapp/templates/myapp/login.html 文件内容:

myapp/templates/myapp/login.html
<!-- myapp/templates/myapp/login.html -->
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
<style>
.errorlist { color: red; list-style-type: none; padding-left: 0; }
label { display: block; margin-top: 10px; }
</style>
</head>
<body>
<h2>Login</h2>
<h2>登录</h2>
<form method="post">
{% csrf_token %}
{{ form.non_field_errors }}
<div>
{{ form.username.label_tag }}
{{ form.username }}
{{ form.username.errors }}
</div>
<div>
{{ form.password.label_tag }}
{{ form.password }}
{{ form.password.errors }}
</div>
<br>
<button type="submit">Login</button>
<button type="submit">登录</button>
</form>
</body>
</html>

关键模板元素:

  • <form method=“post”>:指定 HTTP 方法。
  • {% csrf_token %}:重要的安全标签,用于防止跨站请求伪造(Cross-Site Request Forgery,CSRF)攻击。Django 中间件(middleware)会在 POST 请求时检查此令牌(token)。
  • {{ form.as_p }} 或 {{ form.as_ul }} 或 {{ form.as_table }}:Django 提供的快捷方式,分别将表单字段渲染并包裹在 <p>、<li> 或 <tr><td> 标签中。
  • 手动字段渲染(如上所示):提供了更多控制。{{ form.fieldname.label_tag }}、{{ form.fieldname }}、{{ form.fieldname.errors }} 分别渲染特定字段的标签(label)、输入 widget 和验证错误。
  • {{ form.non_field_errors }} 显示不与特定字段关联的错误(例如,来自 form.add_error(None, …) 的错误)。

myapp/templates/myapp/loggedin.html 文件内容:

myapp/templates/myapp/loggedin.html
<!-- myapp/templates/myapp/loggedin.html -->
<!DOCTYPE html>
<html>
<head>
<title>Logged In</title>
<title>已登录</title>
</head>
<body>
<p>Welcome, **{{ username }}**! (Login successful - demo only)</p>
<p>欢迎, **{{ username }}**! (登录成功 - 仅为演示)</p>
</body>
</html>

在 myapp/urls.py 中将 URL 映射到视图:

myapp/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('login/', views.login_view, name='login'),
# Add other app URLs here
# 在此处添加其他应用(app)的 URL
]

确保你的主项目 urls.py 包含了此 myapp/urls.py。

运行开发服务器(python manage.py runserver)并导航到 /myapp/login/(或者你配置的任何 URL)。

你应该会看到登录表单。尝试提交:

  • 字段为空时:Django 的 required=True 验证应该会触发,在字段旁边显示错误。
  • 提交有效数据时:你应该被重定向到 loggedin.html(或看到其内容)。
  • 登录详细信息不正确时(如果你实现了认证):你会再次看到表单,并显示一个非字段错误消息。

你可以通过定义以 clean_ 开头后跟字段名的方法,或者定义一个通用的 clean() 方法,来为你的表单类添加自定义验证逻辑。

示例:确保用户名不是 ‘admin’。

myapp/forms.py
from django import forms
from django.core.exceptions import ValidationError
class LoginForm(forms.Form):
username = forms.CharField(max_length=100, required=True)
password = forms.CharField(widget=forms.PasswordInput(), required=True)
def clean_username(self):
data = self.cleaned_data['username']
if data.lower() == 'admin':
raise ValidationError("Admin username is not allowed.")
# 管理员用户名不允许。
return data # Always return the cleaned data
# 总是返回已清理的数据
# Example clean() method for cross-field validation
# clean() 方法示例,用于跨字段验证
# def clean(self):
# cleaned_data = super().clean()
# # Add validation that depends on multiple fields
# # 添加依赖于多个字段的验证逻辑
# return cleaned_data

如果某个验证方法抛出 ValidationError,表单的 is_valid() 将返回 False,并且错误消息将与该字段关联(如果在 clean() 中抛出且未指定字段,则通过 non_field_errors 显示)。

进一步阅读:Django 表单文档