1.用处
1、用户请求数据验证
2、自动生成错误信息
3、打包用户提交的正确信息
4、如果其中有一个错误了,其他的正确,则保留上次输入的内容
5、自动创建input标签并可以设置样式
6.基于forms实现增删改查
2.使用方法
对于注册信息进行校验
在应用下创建一个forms.py文件(起什么名无所谓,后面在视图函数中直接导入进来使用)
from django import forms from django.forms import widgets from django.core.exceptions import NON_FIELD_ERRORS, ValidationError class UserInfo(forms.Form): username = forms.CharField( required=True, # 标明是必填字段,不能为空 min_length=4, max_length=12, label="姓名", # 给定字段名称,直接把username换成了姓名 error_messages={"required": "用户名不能为空!!"}, widget=widgets.TextInput(attrs={"placeholder": "用户名", "class": "form-control"}) # 自动生成input框 ) password = forms.CharField( widget=widgets.PasswordInput(attrs={"placeholder":"密码","class":"form-control"}), label="密码", ) sec_pwd = forms.CharField( required=True, label="二次密码", widget=widgets.PasswordInput(attrs={"placeholder":"二次密码","class":"form-control"}) ) age = forms.IntegerField( label="年龄", widget = widgets.NumberInput(attrs={"placeholder":"年龄","class":"form-control"}), error_messages={"required": "输入不对!!"}, ) email = forms.EmailField( widget=widgets.EmailInput(attrs={"placeholder":"邮箱","class":"form-control"}), label="邮箱", error_messages={"invalid": "格式错误"} ) # 定制化二次错误信息 # 局部钩子 # 注意这里必须是以clean_ 开头,可以查看源码,下面得到的val都是字符串类型 def clean_username(self): val = self.cleaned_data.get("username") if not val.isdigit(): return val else: raise ValidationError("用户名非纯数字") def clean_password(self): val = self.cleaned_data.get("password") if len(val) > 3: return val else: raise ValidationError("密码太短") def clean_age(self): val = self.cleaned_data.get("age") if int(val) > 18: return val else: raise ValidationError("岁数太小") # 全局钩子 # 对于两次输入的密码进行校验是否一致 def clean(self): password = self.cleaned_data.get("password") sec_pwd = self.cleaned_data.get("sec_pwd") if password == sec_pwd: return self.cleaned_data else: raise ValidationError("两次密码不一致")
这个类到底干了什么事呢??你在解释器中打印就会发现,第一件事就是将自己显示成HTML。form对象做的第二件事就是来校验信息
调用任何绑定form的is_valid()方法,就可以知道它的数据是否合法
Django的form系统自动寻找匹配的函数方法,该方法名称以 clean_ 开头,并以字段名称结束。 如果有这样的方法,它将在校验时被调用。这属于一次额外校验
如果存在异常,我们抛出一个 forms.ValidationError 型异常,这个异常的描述会被作为错误列表中的一项显示给用户
views.py
from django.shortcuts import render,HttpResponse # Create your views here. from app01.form import UserInfo def index(request): # 定制化提示信息, if request.method=="POST": form = UserInfo(request.POST) # 如果全部输入信息有效 if form.is_valid(): return HttpResponse("添加成功") else: # 打印输入的信息 print("---",form.cleaned_data) # 得到一个字典 print("???",form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]} print("!!!",form.errors.get("email")) # ErrorList ["错误信息",] g_error = form.errors.get("__all__") print("+++",g_error) # <ul class="errorlist nonfield"><li>两次密码不一致</li></ul> if g_error: g_error = g_error[0] # 直接获取你自己的错误提示,即两次密码不一致 return render(request, "index.html", locals()) else: form = UserInfo() return render(request, "index.html", locals())
这里form.py和创建的视图函数衔接关键点就在于 form = UserInfo(request.POST)
模板渲染:
方式一:
以p标签的样式展现出来,当然也能以能table标签展示
index.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> .error { color: red; } </style> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css"> </head> <body> <h3>注册用户</h3> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> {# 这个novalidate很重要啊,没有这个后面自己定制的错误提示无法渲染出来 #} {# 自己编写表单验证插件的时候,使用它可以避免和默认的表单验证冲突 #} <form action="" method="post" novalidate> {% csrf_token %} {{ form.as_p }} <input type="submit"> </form> </div> </div> </div> </body> </html>
# 要注意这里form表单中添加的属性novalidate,它能够避免和默认的表单验证发生冲突
方式二:
书写相对麻烦
<form action="" method="post" novalidate> {% csrf_token %} <div> <p>姓名</p> {{ form.username }} </div> <div> <p>密码</p> {{ form.password }} </div> <div> <p>二次密码</p> {{ form.sec_pwd }} </div> <div> <p>年龄</p> {{ form.age }} </div> <div> <p>年龄</p> {{ form.email }} </div> <input type="submit"> </form>
方式三(常用):
<form action="" method="post" novalidate> {% csrf_token %} {% for foo in form %} <div class="form-group"> {# 渲染出lable标签,标签后面是输入框 #} <lable>{{ foo.label }}</lable>{{ foo }} {# 专门定制报错信息 #} <span class="error pull-right">{{ foo.errors.0 }}</span> {# 对于二次输入报错信息进行设置 #} {% if foo.label == "二次密码" %} <span class="error pull-right">{{ g_error|default_if_none:"" }}</span> {% endif %} </div> {% endfor %} <input type="submit" class="btn btn-default btn-sm"> </form>
3.基于forms组件的ModelForm实现的增删改查
models.py
from django.db import models class Depart(models.Model): caption = models.CharField(max_length=32) def __str__(self): return self.caption class Role(models.Model): title = models.CharField(max_length=32) def __str__(self): return self.title class User(models.Model): name = models.CharField(verbose_name='姓名',max_length=32) depart = models.ForeignKey(verbose_name='部门',to='Depart',on_delete=models.CASCADE) gender_choices = ( (1,'男'), (2,'女'), ) gender = models.IntegerField(verbose_name='性别',choices=gender_choices,default=1) roles = models.ManyToManyField(verbose_name='角色',to='Role')
urls.py
from django.contrib import admin from django.urls import path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), re_path('user/list/$', views.user_list), re_path('user/add/$', views.user_add), re_path('user/edit/(?P<uid>\d+)/$', views.user_edit), re_path('user/del/(?P<uid>\d+)/$', views.user_del), ]
views.py
from django.shortcuts import render,redirect from app01 import models def user_list(request): user_queryset = models.User.objects.all() return render(request,'user_list.html',{'user_queryset':user_queryset}) from django import forms class UserForm(forms.ModelForm): class Meta: model = models.User fields = '__all__' # fields = ['name','depart'] widgets = { 'name':forms.TextInput(attrs={'class':'form-control'}), 'depart':forms.Select(attrs={'class':'form-control'}), 'gender':forms.Select(attrs={'class':'form-control'}), 'roles':forms.SelectMultiple(attrs={'class':'form-control'}), } error_messages = { 'name':{ 'required':'用户名不能为空' } } def user_add(request): if request.method == "GET": form = UserForm() else: form = UserForm(request.POST) if form.is_valid(): print('通过验证') form.save() return redirect('/user/list/') return render(request,'user_add.html',{'form':form}) def user_edit(request,uid): obj = models.User.objects.filter(id=uid).first() if request.method =='GET': form = UserForm(instance=obj) return render(request,'user_edit.html',{'form':form}) else: form = UserForm(data=request.POST,instance=obj) if form.is_valid(): form.save() return redirect('/user/list/') else: return render(request, 'user_edit.html', {'form': form}) def user_del(request,uid): models.User.objects.filter(id=uid).delete() return redirect('/user/list/')
user_list.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container"> <a href="/user/add/" class="btn btn-primary">添加</a> <table class="table table-bordered"> <thead> <tr> <th>名称</th> <th>性别</th> <th>部门</th> <th>角色</th> <th>操作</th> </tr> </thead> <tbody> {% for row in user_queryset %} <tr> <td>{{ row.name }}</td> <td>{{ row.get_gender_display }}</td> <td>{{ row.depart.caption }}</td> <td> {% for node in row.roles.all %} <span>{{ node.title }}</span> {% endfor %} </td> <td> <a href="/user/edit/{{ row.id }}/">编辑</a> <a href="/user/del/{{ row.id }}/">删除</a> </td> </tr> {% endfor %} </tbody> </table> </div> </body> </html>
user_edit.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <h1>编辑用户</h1> <form method="post" novalidate> {% csrf_token %} {% for field in form %} <div>{{ field.label }}{{ field }} {{ field.errors.0 }}</div> {% endfor %} <input type="submit" value="提交"> </form> </body> </html>
user_add.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container"> <h1>添加用户</h1> <form method="post" class="form-horizontal" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group"> <label class="col-sm-2 control-label">{{ field.label }} </label> <div class="col-sm-10"> {{ field }} {{ field.errors.0 }} </div> </div> {% endfor %} <input type="submit" value="提交"> </form> </div> </body> </html>
效果:
这里其他效果: