11.Django高级之forms组件
forms组件之校验字段
# 第一步:定义一个类,继承forms.Form # 第二步:在类中写字段,要校验的字段,字段属性就是校验规则 # 第三步:实例化得到一个Form对象,把要校验的数据传入 # 第四步:调用register_form.is_valid()校验,校验通过就是True # 第五步:校验通过有register_form.cleaned_data # 第六步:校验不通过 register_form.errors
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册用户</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <div class="col-md-6 col-md-offset-3"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">注册功能</h3> </div> <div class="panel-body"> <h1 class="text-center">注册</h1> <form action="" method="post"> <p>用户名:<input type="text" name="name" class="form-control"></p> <p>密码:<input type="password" name="password" class="form-control"></p> <p>确认密码:<input type="password" name="re_password" class="form-control"></p> <p>邮箱:<input type="text" name="email" class="form-control"></p> <input id="id_btn" type="submit" class="btn btn-primary btn-block"> </div> </div> </div> </body> </html> 校验手动渲染示例html
class User(models.Model): name = models.CharField(max_length=32,verbose_name='用户名') password = models.CharField(max_length=32,verbose_name='密码') email = models.EmailField(verbose_name='邮箱')
# views.py
from django.shortcuts import render
from django import forms
#定义类
class RegisterForm(forms.Form):
# name字符串类型最大8位,最小3位
name = forms.CharField(max_length=8, min_length=3, label='用户名')
# password字符串类型最大8位,最小3位
password = forms.CharField(max_length=8, min_length=3, label='密码')
# re_password字符串类型最大8位,最小3位
re_password = forms.CharField(max_length=8, min_length=3, label='确认密码')
# email必须符合邮箱格式,xxx@xx.com
email = forms.EmailField(label='邮箱')
#在视图中使用
register_form = RegisterForm(request.POST)
if register_form.is_valid():
# 校验通过,存
# 取出校验通过的数据
print('校验通过')
print(register_form.cleaned_data)
# 存储前先删除多余的字段
register_form.cleaned_data.pop('re_password')
# 将数据存入数据库的user表中
models.User.objects.create(**register_form.cleaned_data)
else:
# 校验不通过
print('校验不通过')
print(register_form.errors)
def register(request): if request.method == 'GET': # GET请求没有数据,需要生成一个空form对象 # 这个form跟下面没有关系,是get请求过来的得到一个空form register_form = RegisterFrom() # 传到前端页面后,通过form进行渲染 return render(request, 'register.html', {'form': register_form}) else: register_form = RegisterFrom(request.POST) if register_form.is_valid(): print('效验通过') print(register_form.cleaned_data) register_form.cleaned_data.pop('re_password') models.User.objects.create(**register_form.cleaned_data) else: print('效验不通过') print(register_form.errors) return render(request,'register.html') 视图层:views.py
<h2>通过form自动渲染一</h2>
<form action="" method="post">
<p>用户名 {{ form.name }}</p>
<p>密码 {{ form.password }}</p>
<p>确认密码 {{ form.re_password }}</p>
<p>邮箱 {{ form.email }}</p>
<input type="submit" value="提交"></form>
渲染方式二
推荐使用,代码书写简单,并且可扩展性强
<h2>通过form自动渲染二(基本用这种)</h2>
<form action="" method="post">
{% for item in form %}
<p>{{ item.label }}{{ item }}</p>
{% endfor %}
<input type="submit" value="提交"><span style="color: red">{{ error }}</span>
</form>
渲染方式三
代码书写极少,封装程度太高,不便于后续的扩展,一般情况下只在本地测试使用
<h2>通过form自动渲染三</h2>
<form action="" method="post">
{{ form.as_p }}
{# {{ form.as_table }}#}
{# {{ form.as_ul }}#}
</form>
前端渲染代码:(全部采用方式二)
from django import forms from django.forms import widgets class RegisterForm(forms.Form): name = forms.CharField(max_length=8, min_length=3, label='用户名', error_messages={ 'max_length': '用户名最长为8位', 'min_length': '用户名最短为3位', 'required': '用户名不能为空位' }, widget=widgets.TextInput(attrs={'class':'form-control'})) password = forms.CharField(max_length=8, min_length=3, label='密码', error_messages={ 'max_length': '密码最长为8位', 'min_length': '密码最短为3位', 'required': '密码不能为空' }, widget=widgets.PasswordInput(attrs={'class':'form-control'})) re_password = forms.CharField(max_length=8, min_length=3, label='确认密码', error_messages={ 'max_length' : '密码最长为8位', 'min_length' : '密码最短为3位', 'required' : '密码不能为空' }, widget=widgets.PasswordInput(attrs={'class':'form-control'})) email = forms.EmailField(label='邮箱', error_messages={'required': '邮箱不能为空', 'invalid': '邮箱格式不正确'}, widget=widgets.TextInput(attrs={'class':'form-control'})) # views视图函数处理部分: def register(request): if request.method == 'GET': register_form = RegisterForm() return render(request, 'register.html', {'form': register_form}) else: register_form = RegisterForm(request.POST) if register_form.is_valid(): print('校验通过') print(register_form.cleaned_data) register_form.cleaned_data.pop('re_password') models.User.objects.create(**register_form.cleaned_data) return HttpResponse('ok') else: # 校验不通过 print('校验不通过') print(register_form.errors) return render(request, 'register.html', {'form': register_form})

forms组件参数配置
class Ret(Form):
name = forms.CharField(max_length=10, min_length=2, label='用户名',
error_messages={
'required': '该字段不能为空',
'invalid': '格式错误',
'max_length': '太长',
'min_length': '太短'},
widget=widgets.TextInput(attrs={'class':'form-control'}))
-局部钩子
-def clean_字段名(self):
-校验规则
-如果通过,return 值
-如果不通过,抛异常
-全局钩子(多个字段校验)
-def clean(self):
-如果通过,return clean_data
-如果不通过,抛异常
局部钩子
def clean_name(self): # name字段的局部钩子
# 获取用户输入的用户名
name = self.cleaned_data.get('name')
# 校验名字不能以sb开头
if name.startswith('sb'):
# 校验不通过,必须抛异常,
raise ValidationError('不能以sb开头')
else:
# 校验通过,再返回name对应的值
return name

全局钩子
def clean(self): # 全局钩子
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if re_password != password:
# 校验不通过
self.add_error('re_password','两次密码不一致')
else:
# 局部钩子拿什么返回什么,全局钩子所有都返回
return self.changed_data

-使用步骤:
-写一个类,继承Form类
-写字段,字段参数(限制该字段的长短)
-错误信息中文:字段参数
-widget:控制生成标签的属性
-视图函数中:
-实例化得到form对象时,把要校验的数据传入
-is_valid():clean_data和errors就有值了
-如果校验通过就存,不通过就给页面提示
-渲染页面
-for循环的方式渲染页面(在标签前后可以再加标签)
from django.db import models # 创建用户表 class User(models.Model): name = models.CharField(max_length=32,verbose_name='用户名') password = models.CharField(max_length=32,verbose_name='密码') email = models.EmailField(verbose_name='邮箱') 模型层models.py
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^register/$',views.register) ]
from django import forms from django.forms import widgets from django.core.exceptions import ValidationError class RegisterForm(forms.Form): name = forms.CharField(max_length=8, min_length=3, label='用户名', error_messages={ 'max_length': '用户名最长为8位', 'min_length': '用户名最短为3位', 'required': '用户名不能为空位' }, widget=widgets.TextInput(attrs={ 'class':'form-control' })) password = forms.CharField(max_length=8, min_length=3, label='密码', error_messages={ 'max_length': '密码最长为8位', 'min_length': '密码最短为3位', 'required': '密码不能为空' }, widget=widgets.PasswordInput(attrs={ 'class':'form-control' })) re_password = forms.CharField(max_length=8, min_length=3, label='确认密码', error_messages={ 'max_length' : '密码最长为8位', 'min_length' : '密码最短为3位', 'required' : '密码不能为空' }, widget=widgets.PasswordInput(attrs={ 'class':'form-control' })) email = forms.EmailField(label='邮箱', error_messages={ 'required': '邮箱不能为空', 'invalid': '邮箱格式不正确' }, widget=widgets.TextInput(attrs={ 'class':'form-control' def clean_name(self): # name字段的局部钩子 # 获取用户输入的用户名 name = self.cleaned_data.get('name') # 校验名字不能以sb开头 if name.startswith('sb'): # 校验不通过,必须抛异常, raise ValidationError('不能以sb开头') else: # 校验通过,再返回name对应的值 return name def clean(self): # 全局钩子 password = self.cleaned_data.get('password') re_password = self.cleaned_data.get('re_password') if re_password != password: # 校验不通过 self.add_error('re_password','两次密码不一致') else: # 局部钩子拿什么返回什么,全局钩子所有都返回 return self.changed_data from app01 import models def register(request): if request.method == 'GET': # GET请求没有数据,需要生成一个空form对象 # 这个form跟下面没有关系,是get请求过来的得到一个空form register_form = RegisterFrom() # 传到前端页面后,通过form进行渲染 return render(request, 'register.html', {'form': register_form}) else: # 实例化得到对象,传入要校验的数据 register_form = RegisterForm(request.POST) if register_form.is_valid(): # 校验通过,存 # 取出校验通过的数据 print('校验通过') print(register_form.cleaned_data) register_form.cleaned_data.pop('re_password') models.User.objects.create(**register_form.cleaned_data) return HttpResponse('ok') else: # 校验不通过 print('校验不通过') print(register_form.errors) return render(request, 'register.html', {'form': register_form})
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
class RegisterForm(forms.Form):
name = forms.CharField(max_length=8, min_length=3, label='用户名',
error_messages={
'max_length': '用户名最长为8位',
'min_length': '用户名最短为3位',
'required': '用户名不能为空位'
},
widget=widgets.TextInput(attrs={
'class':'form-control'
}))
password = forms.CharField(max_length=8, min_length=3, label='密码',
error_messages={
'max_length': '密码最长为8位',
'min_length': '密码最短为3位',
'required': '密码不能为空'
},
widget=widgets.PasswordInput(attrs={
'class':'form-control'
}))
re_password = forms.CharField(max_length=8, min_length=3, label='确认密码',
error_messages={
'max_length' : '密码最长为8位',
'min_length' : '密码最短为3位',
'required' : '密码不能为空'
},
widget=widgets.PasswordInput(attrs={
'class':'form-control'
}))
email = forms.EmailField(label='邮箱',
error_messages={
'required': '邮箱不能为空',
'invalid': '邮箱格式不正确'
},
widget=widgets.TextInput(attrs={
'class':'form-control'
def clean_name(self): # name字段的局部钩子
# 获取用户输入的用户名
name = self.cleaned_data.get('name')
# 校验名字不能以sb开头
if name.startswith('sb'):
# 校验不通过,必须抛异常,
raise ValidationError('不能以sb开头')
else:
# 校验通过,再返回name对应的值
return name
def clean(self): # 全局钩子
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if re_password != password:
# 校验不通过
self.add_error('re_password','两次密码不一致')
else:
# 局部钩子拿什么返回什么,全局钩子所有都返回
return self.changed_data
from app01 import models
def register(request):
if request.method == 'GET':
# GET请求没有数据,需要生成一个空form对象
# 这个form跟下面没有关系,是get请求过来的得到一个空form
register_form = RegisterFrom()
# 传到前端页面后,通过form进行渲染
return render(request, 'register.html', {'form': register_form})
else:
# 实例化得到对象,传入要校验的数据
register_form = RegisterForm(request.POST)
if register_form.is_valid():
# 校验通过,存
# 取出校验通过的数据
print('校验通过')
print(register_form.cleaned_data)
register_form.cleaned_data.pop('re_password')
models.User.objects.create(**register_form.cleaned_data)
return HttpResponse('ok')
else:
# 校验不通过
print('校验不通过')
print(register_form.errors)
return render(request, 'register.html', {'form': register_form})
1 为什么局部钩子要写成 clean_字段名,为什么要抛异常
2 入口在 is_valid()
3 校验流程
-先校验字段自己的规则(最大,最小,是否必填,是不是合法)
-校验局部钩子函数
-全局钩子校验
4 流程
is_valid()---》return self.is_bound and not self.errors
self.errors:方法包装成了数据数据
一旦self._errors有值,就不进行校验了(之前调用过了)
self.full_clean():核心
self._errors = ErrorDict()
if not self.is_bound:
return
self.cleaned_data = {}
self._clean_fields()
self._clean_form()
self._post_clean()
self._clean_fields():核心代码,局部钩子执行位置
value = field.clean(value)# 字段自己的校验规则
self.cleaned_data[name] = value #把校验后数据放到cleaned_data
if hasattr(self, 'clean_%s' % name): # 判断有没有局部钩子
value = getattr(self, 'clean_%s' % name)() #执行局部钩子
self.cleaned_data[name] = value #校验通过,把数据替换一下
# 如果 校验不通过,会抛异常,会被捕获,捕获后执行
self.add_error(name, e)
def _clean_form(self):#全局钩子执行位置
def _clean_form(self):
try:
#如果自己定义的form类中写了clean,他就会执行
cleaned_data = self.clean()
except ValidationError as e:
self.add_error(None, e)
from django.urls import path from app01 import views urlpatterns = [ path('user/add/', views.user_add), ]
from django.shortcuts import render, redirect, HttpResponse from app01.models import Department, UserInfo from django import forms # 自定义一个类,继承ModelForm类 class UserModelForm(forms.ModelForm): class Meta: model = UserInfo # 对应model中的类 fields = ["name", "password", "age", "account", "create_time", "gender", "depart"] # fields = ['字段1','字段2',...] 或者这种用法,把需要展示的字段添加到这个列表中 # fields = '__all__' # 获取所有字段 # 根据源码重写父类方法 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 循环找到所有字段的插件,添加class = "form-control" for name, field in self.fields.items(): # 不想让某个字段用样式可以做判断 # if name == "password": # continue field.widget.attrs = {"class": "form-control"} # 可以自定义属性 def user_add(request): user_data = UserModelForm() # 然后在url对应的视图函数中实例化这个类,把这个对象传给前端 return render(request, 'user_add.html', {"user_data": user_data}) views.py
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.css' %}">
<script src="{% static 'js/jQuery3.6.js' %}"></script>
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.js' %}"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title text-center">
<span class="glyphicon glyphicon-plus"></span>
添加用户
</h2>
</div>
<div class="panel-body">
<form method="post">
{% csrf_token %}
{% for obj in user_data %}
<div class="form-group">
<label>{{ obj.label }}</label>
{{ obj }}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary btn-block">添 加</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
user_add.html
from django.db import models class Department(models.Model): title = models.CharField(max_length=64, verbose_name="部门名称") def __str__(self): return self.title class UserInfo(models.Model): name = models.CharField(max_length=32, verbose_name="姓名") password = models.CharField(max_length=64, verbose_name="密码") age = models.IntegerField(verbose_name="年龄") account = models.DecimalField(max_digits=10, decimal_places=2, default=0, verbose_name="账户余额") create_time = models.DateTimeField(verbose_name="入职时间") # 级联删除 depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE, verbose_name="部门") gender_choices = ( (1, "男"), (2, "女") ) gender = models.SmallIntegerField(choices=gender_choices, verbose_name="性别") models.py







浙公网安备 33010602011771号