总任务:
- 使用Forms组件解决用户输入错误、空,并且展示错误信息在输入框的下面
- 解决用户校验,以及用户提交之后还可以将第一次输入的东西继续展示到页面
1.示例
1.1 先导入forms第三方模块包,并且写入我们前端组要校验的字段
```
from django import forms
class LoginForm(forms.Form):
role = forms.ChoiceField(
required=True, # 校验输入是否为空
label='角色',
choices=(('2', '客户'), ('1', '管理员')),
widget=forms.Select(attrs={"class": "form-control"}) # attr这里可以写前端的样式
)
# <input type = "text" class ="form-control" placeholder="用户名" name="username" >
username = forms.CharField(
required=True,
label='用户名',
widget=forms.TextInput(attrs={"class": "form-control", "placeholder": "用户名"})
)
password = forms.CharField(
required=True,
label='密码',
widget=forms.PasswordInput(attrs={"class": "form-control", "placeholder": "密码"})
)
```
1.2 在views.py函数里面实例化一下此类
```
def login(request):
if request.method == "GET":
form = LoginForm()
return render(request, "login.html", {"form": form})
```
- 在前端进行渲染,将username文本框进行替换
1.3 is_valid()校验数据
![img](/i/l/?n=24&i=blog/3339660/202410/3339660-20241006165535136-587355920.png)
```
# 如果校验不成功,则继续返回给页面原本的数据,如果校验成功,则返回一个校验成功之后数据的字典
```
1.4使用到form组件总结
```
class LoginForm(forms.Form):
role = forms.ChoiceField(
required=True,
choices=(("2", "客户"), ("1", "管理员")),
widget=forms.Select(attrs={"class": "form-control"})
)
username = forms.CharField(
initial="wupeiqi",
required=True,
正则表达式
widget=forms.TextInput(attrs={"class": "form-control", "placeholder": "用户名"})
)
# 自定义方法(钩子)
def clean_username(self):
raise 异常
return 123
form = LoginForm(initial={"username":"xxx","password":"xx"})
- 生成HTML标签 + 携带数据
- 保留原来提交的数据,不再担心form表单提交时页面刷新。
- 显示默认值,做编辑页面显示默认值。
- 数据校验,对用户提交的数据格式校验
form = LoginForm(data=request.POST)
if form.is_valid():
print(form.cleaned_data)
else:
print(form.errors)
```
2.优化改进(及所需使用form组件功能)
2.1 前端加入for循环,解决字段过多导致代码冗余
# ==================================第一部分==================================
class LoginForm(forms.Form):
role = forms.ChoiceField(
required=True,
label='角色',
choices=(('2', '客户'), ('1', '管理员')),
widget=forms.Select(attrs={"class": "form-control"})
)
# <input type = "text" class ="form-control" placeholder="用户名" name="username" >
username = forms.CharField(
required=True,
label='用户名',
widget=forms.TextInput(attrs={"class": "form-control", "placeholder": "用户名"})
)
password = forms.CharField(
required=True,
label='密码',
widget=forms.PasswordInput(attrs={"class": "form-control", "placeholder": "密码"}),
render_value=True
)
==================================第二部分==================================
{% for field in form %}
<div class="form-group">
<label>{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
2.2 forms组件类中加入限制字段的数量,以及加入错误提示
2.3 form校验的流程
-
每个字段的内部:required + validators +
min_length=6,max_length=10
-
字段的钩子方法
def clean_username(self): user = self.cleaned_data['username'] # 校验规则 # 校验失败 if len(user) < 3: from django.core.exceptions import ValidationError raise ValidationError("用户名格式错误") return user print(form.cleaned_data)
-
clean
def clean(self): # 对所有值进行校验 from django.core.exceptions import ValidationError # 1.不返回值,默认 self.cleaned_data # 2.返回值,self.cleaned_data=返回的值 # 3.报错,ValidationError -> self.add_error(None, e)
-
_post_clean
def _post_clean(self): pass
2.4 form钩子的实际运用
```
class LoginForm(forms.Form):
role = forms.ChoiceField(
required=True,
label='角色',
choices=(('2', '客户'), ('1', '管理员')),
widget=forms.Select(attrs={"class": "form-control"})
)
# <input type = "text" class ="form-control" placeholder="用户名" name="username" >
username = forms.CharField(
required=True,
label='用户名',
widget=forms.TextInput(attrs={"class": "form-control", "placeholder": "用户名"})
)
password = forms.CharField(
required=True,
label='密码',
widget=forms.PasswordInput(attrs={"class": "form-control", "placeholder": "密码"}),
render_value=True
)
def clean_username(self):
user = self.cleaned_data['username']
# 校验规则
# 校验失败
if len(user) < 3:
from django.core.exceptions import ValidationError
raise ValidationError("用户名格式错误")
return user
def clean_password(self):
return md5(self.cleaned_data['password'])
def clean(self):
# 对所有值进行校验,无论前面的字段校验成功与否
user = self.cleaned_data.get('username')
pwd = self.cleaned_data.get('password')
if user and pwd:
pass
from django.core.exceptions import ValidationError
# 1.不返回值,默认 self.cleaned_data
# 2.返回值,self.cleaned_data=返回的值
# 3.报错,ValidationError -> self.add_error(None, e)
# print(self.cleaned_data)
# raise ValidationError("整xxxxx体错误")
def _post_clean(self):
pass
```
2.5 如果想要让错误展示在某个字段的下面(form.add.error)
2.6 form校验源码解析
2.7 登录界面代码最终版
- 总共分为3个文件
2.7.1 前端login.html文件
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.css' %}">
<style>
.box {
width: 450px;
border: 1px solid #f0f0f0;
margin-left: auto;
margin-right: auto;
margin-top: 100px;
padding-left: 40px;
padding-right: 40px;
padding-bottom: 30px;
box-shadow: 5px 10px 10px rgb(0 0 0 / 5%);
}
</style>
</head>
<body>
<div class="box">
<h2 style="text-align: center;">用户登录</h2>
<form method="post">
{% csrf_token %}
{% for field in form %}
<div class="form-group" style="position: relative;margin-bottom: 25px">
<label>{{ field.label }}</label>
{{ field }}
<span style="color: red;position: absolute;">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">登 录</button>
<a href='{% url 'sms_login' %}' style="float: right;">短信登录</a>
</form>
</div>
</body>
</html>
2.7.2 后端account.py文件
# -*- coding utf-8 -*-
import random
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.http import JsonResponse
from django.shortcuts import render, redirect
from django_redis import get_redis_connection
from web import models
from utils.encrypt import md5
from utils import tencent
from django import forms
"""
@version : python 3.10
@author : T-mars
@file : xxx.py
@time : 2023-10-28 下午13:20
"""
class LoginForm(forms.Form):
role = forms.ChoiceField(
required=True,
label='角色',
choices=(('2', '客户'), ('1', '管理员')),
widget=forms.Select(attrs={"class": "form-control"})
)
# <input type = "text" class ="form-control" placeholder="用户名" name="username" >
username = forms.CharField(
required=True,
label='用户名',
widget=forms.TextInput(attrs={"class": "form-control", "placeholder": "用户名"})
)
password = forms.CharField(
required=True,
label='密码',
widget=forms.PasswordInput(attrs={"class": "form-control", "placeholder": "密码"}),
# render_value=True
)
def clean_username(self):
user = self.cleaned_data['username']
# 校验规则
# 校验失败
if len(user) < 3:
from django.core.exceptions import ValidationError
raise ValidationError("用户名格式错误")
return user
def clean_password(self):
return md5(self.cleaned_data['password'])
def clean(self):
# 对所有值进行校验,无论前面的字段校验成功与否
user = self.cleaned_data.get('username')
pwd = self.cleaned_data.get('password')
if user and pwd:
pass
from django.core.exceptions import ValidationError
# 1.不返回值,默认 self.cleaned_data
# 2.返回值,self.cleaned_data=返回的值
# 3.报错,ValidationError -> self.add_error(None, e)
# print(self.cleaned_data)
# raise ValidationError("整xxxxx体错误")
def _post_clean(self):
pass
class SmsLoginForm(forms.Form):
role = forms.ChoiceField(
required=True,
label='角色',
choices=(('2', '客户'), ('1', '管理员')),
widget=forms.Select(attrs={"class": "form-control"})
)
# <input type = "text" class ="form-control" placeholder="用户名" name="username" >
mobile = forms.CharField(
required=True,
label='手机号',
widget=forms.TextInput(attrs={"class": "form-control", "placeholder": "手机号"})
)
code = forms.CharField(
required=True,
label='短信验证码',
widget=forms.PasswordInput(attrs={"class": "form-control", "placeholder": "短信验证码"})
)
# return render(request, 'login.html')
def login(request):
if request.method == "GET":
form = LoginForm()
return render(request, "login.html", {"form": form})
# 1.接收并获取数据(数据格式或是否为空验证 - Form组件 & ModelForm组件)
form = LoginForm(data=request.POST)
if not form.is_valid():
return render(request, "login.html", {"form": form})
# 2.去数据库校验 1管理员 2客户
data_dict = form.cleaned_data
role = data_dict.pop('role')
if role == "1":
user_object = models.Administrator.objects.filter(active=1).filter(**data_dict).first()
else:
user_object = models.Customer.objects.filter(active=1).filter(**data_dict).first()
# 2.1 校验失败
if not user_object:
form.add_error("username", "用户名或密码错误")
return render(request, "login.html", {'form': form})
# 2.2 校验成功,用户信息写入session+进入项目后台
mapping = {"1": "ADMIN", "2": "CUSTOMER"}
request.session['user_info'] = {'role': mapping[role], 'name': user_object.username, 'id': user_object.id}
return redirect('/home/')