form表单使用(博客系统的登陆验证,注册)
先从小的实例来看form的用法
登陆验证实例,来看form的常规用法
1. forms.py
1 # 用于登陆验证验证 2 from django.core.validators import RegexValidator 支持正则 3 # 登陆验证手动触发错误对象 4 from django.core.exceptions import ValidationError 5 6 # 登陆规则 7 class loginform(Form): 8 name = fields.CharField( 9 required=True, 10 min_length=3, 11 max_length=18, 12 error_messages={ 13 'required':'用户不能为空', 14 'min_length':'用户长度不能小于3', 15 'max_length':'用户长度不能大于18', 16 } 17 ) 18 pwd = fields.CharField( 19 required=True, 20 min_length=3, 21 max_length=18, 22 error_messages={ 23 'required': '用户不能为空', 24 'min_length': '用户长度不能小于3', 25 'max_length': '用户长度不能大于18', 26 'invalid':'密码格式错误', #自定义错误,优先级高 27 },validators=[RegexValidator('\d+','只能是数字',code)] #只能是数字也是报错信息,不过优先级低,也就是说如果用户输入非数字,那么提示invalid错误,如果invalid没有,那么报‘只能是数字’错误,code就是给invalid重新命名而已,没有什么用处,可以不写) 28 29 30 # from扩展,可以直接在里面进行数据库操作,场景注册,局部钩子,博客系统注册,详细介绍了用法,见下文####### 31 def clean_name(self): 32 # 验证可以直接写在这里,进行验证,返回可以是任意类型,重新赋值给clean_date里面的user 33 user = self.cleaned_data['name'] 34 is_exsit = models.UserInfo.objects.filter(name=user).count() 35 if not is_exsit: 36 raise ValidationError('用户名不存在') 37 return user 38 39 def clean_pwd(self): 40 # 这里可以匹配数据库其他校验,返回可以是任意类型,重新赋值给clean_date里面的pwd 41 return True
2. views.py
1 def login(request): 2 if request.method=='GET': 3 form = loginform() 4 return render(request,'login.html',{'form':form}) 5 elif request.method=='POST': 6 form = loginform(request.POST) # 前端提交数据给form 7 # 规则验证 8 if form.is_valid(): 9 # 数据库验证 10 print('name',form.cleaned_data['name']) 11 user_obj = models.UserInfo.objects.filter(**form.cleaned_data).first() 12 if not user_obj: 13 form.add_error('pwd',ValidationError('用户名或者密码错误')) #当你符合格式,但是数据库没有的时候就会报错,跟局部钩子触发错误是一样的 14 return render(request,'login.html',{'form':form}) 15 else: 16 print(form.cleaned_data['name']) 17 work_boj = models.Workers.objects.filter(utow__name=form.cleaned_data['name'], utow__pwd=form.cleaned_data['pwd']).first() 18 work = work_boj.workname 19 request.session[settings.USEROBJ] = {'username': user_obj.name, 'work': work} 20 return redirect('/index/') 21 else: 22 return render(request,'login.html',{'form':form}) 23 else: 24 return HttpResponse('访问请求非get,post')
3. login.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>Title</title> 8 </head> 9 <body> 10 <form method="POST"> 11 {% csrf_token %} 12 <p>{{ form.username }} {{ form.errors.username.0 }} </p> 13 <p>{{ form.password }} {{ form.errors.password.0 }} </p> 14 <input type="submit" value="提交" /> 15 </form> 16 </body> 17 </html>
通过问卷的创建修改来看form的高级用法forms.fields.ChoiceField
主要了解下 forms.fields.ChoiceField(choices=[(1,'a'),(2,'b')]),和初始化数据
models.py
1 class UserInfo(models.Model): 2 """ 3 员工表 4 """ 5 name = models.CharField(max_length=32) 6 7 def __str__(self): 8 return self.name 9 10 class ClassInfo(models.Model): 11 """ 12 班级表 13 """ 14 name = models.CharField(max_length=32) 15 def __str__(self): 16 return self.name 17 18 class Questionnaire(models.Model): # 问卷: 班级 :创建者 19 """ 20 问卷表 21 22 """ 23 title = models.CharField(max_length=64) 24 cls = models.ForeignKey(to=ClassInfo) 25 creator = models.ForeignKey(to=UserInfo) 26 27 def __str__(self): 28 return self.title
1. forms.py
1 from django import forms 2 from django.forms import widgets # 插件,可以插入些样式 3 from django.core.exceptions import ValidationError # 手动触发错误 4 from django.core.validators import RegexValidator # 支持正则 5 6 from app01 import models 7 8 from django.forms.models import ModelChoiceField # 下拉框实时刷新第二种方法 9 10 class questionnaire_form(forms.Form): # 问卷规则 11 title = forms.fields.CharField(required=True, 12 error_messages={'required':'名称不能为空','invalid':'格式错误'}, 13 widget=widgets.TextInput(attrs={'placeholder':'问卷名称', 14 'class':'form-control questionnaire_title'},)) 15 16 # 其他框使用 17 # password = forms.fields.CharField(required=True, error_messages={'required': '密码不能为空'}, 18 #widget=widgets.TextInput(attrs={'placeholder': '密码', 'class': 'form-control'})) # 不能为空 19 # email = forms.fields.EmailField(required=True, error_messages={'required': '邮箱不能为空', 'invalid': '邮箱格式错误'}, 20 # widget=widgets.PasswordInput(attrs={'placeholder': '邮箱', 'class': 'form-control'})) # 不能为空,且邮箱格式 21 # text = forms.fields.CharField(required=True,error_messages={'required':'密码不能为空'}, 22 # widget=widgets.Textarea(attrs={'class':'textatea'})) 23 # cls_list = fields.MultipleChoiceField(choices=[]) # 多选框,传过来的值,是列表 24 # def __init__(self, *args, **kwargs): 25 # super().__init__(*args, **kwargs) 26 # self.fields['cls_list'].choices = models.ClassList.objects.values_list('id', 'caption') 27 28 # 第一种实时刷新,通过重构init方法,不需要models的str方法 29 # cls_id = forms.fields.ChoiceField(choices=[]) 30 # creator_id = forms.fields.ChoiceField(choices=[]) 31 # def __init__(self,*args,**kwargs): 32 # super().__init__(*args,**kwargs) 33 # 不需要重启,修改后提交后,再点进来可以保证修改完成 34 # self.fields['cls_id'].choices= models.ClassInfo.objects.all().values_list('id','name') 35 # self.fields['creator_id'].choices = models.UserInfo.objects.all().values_list('id','name') 36 37 # 第二种实时刷新,需要models里面的str方法,不然下拉框都是对象,访问数据库因为返回的是对象 38 cls_id = ModelChoiceField(queryset=models.ClassInfo.objects.all()) 39 creator_id = ModelChoiceField(queryset=models.UserInfo.objects.all()) 40 41 42 # 局部钩子 43 def clean_title(self): # 针对添加的时候,如果有questionnaire_title,就存在,没有可以正常进行 44 questionnaire_title = models.Questionnaire.objects.filter(title=self.cleaned_data.get('title')) # 前端传来的数据保存在clean_data里 45 if not questionnaire_title: 46 return self.cleaned_data.get('title') 47 else: 48 raise ValidationError('用户名已经存在') # 触发错误
2. views.py
1 from django.shortcuts import render,HttpResponse,redirect 2 3 # Create your views here. 4 5 from .forms import * 6 from app01 import models 7 8 def questionnaire_list(request): 9 ''' 10 问卷首页,格式:8000/questionnaire首页, 11 :param request: 12 :param questionnaire_id: 13 :return: 14 ''' 15 questionnaire_list = models.Questionnaire.objects.all() # 所有问卷 16 return render(request, "questionnaire_list.html", {'questionnaire_list':questionnaire_list}) 17 18 19 def add_questionnaire_obj(request,**kwargs): 20 ''' 21 添加问卷 22 :param request: 23 :param kwargs: 24 :return: 25 ''' 26 if request.method == 'GET': 27 form = questionnaire_form() 28 return render(request, 'add_questionnaire_obj.html',{'form':form}) 29 elif request.method == 'POST': 30 form = questionnaire_form(request.POST) 31 print(request.POST) 32 # 校验 33 if form.is_valid(): 34 print(form.cleaned_data) 35 # 下拉框第二种实时更新方法,因为前端是queryset对象,所以需要把cleand_data数据重新更改一下 36 # form.cleaned_data['creator_id'] = form.cleaned_data['creator_id'].id 37 # form.cleaned_data['cls_id'] = form.cleaned_data['cls_id'].id 38 models.Questionnaire.objects.create(**form.cleaned_data) 39 return redirect('/questionnaire/') 40 else: 41 return render(request, 'add_questionnaire_obj.html', {'form': form}) 42 43 44 45 def edit_questionnaire_obj(request,**kwargs): 46 ''' 47 问卷对象,格式:8000/questionnaire/questionnaire_id,选中问卷编辑 48 :param request: 49 :param kwargs: 50 :return: 51 ''' 52 questionnaire_id = kwargs.get('questionnaire_id') # 得到需要编辑的问卷的ID 53 if request.method == 'GET': 54 questionnaire_obj = models.Questionnaire.objects.filter(id=questionnaire_id).first() # 通过ID找到问卷对象 55 # form的实例化,initial初始化,适用于初始默认的值, 56 # form = questionnaire_form(),没有数据,只是可以在前端渲染标签,需要在FORM里面执行,例如TEXTIINPUT 57 # form = questionnaire_form(data=request.POST),有数据,并且到FORM里面进行验证 58 # 初始化的值,initial={'对象字段名称':,'对象字段,如果是关联字段加上ID,也就是数据库字段是什么,就是什么':} 59 # 初始化的字段名称最好跟MODELS的一样,FORM验证最好也是一样,前端也是一样,这样确保可以正常 ########### 注意 60 form = questionnaire_form(initial={"title":questionnaire_obj.title, "cls_id":questionnaire_obj.cls_id,"creator_id":questionnaire_obj.creator_id}) # 默认值 61 return render(request, "edit_questionnaire_obj.html", {'form': form}) 62 if request.method == 'POST': 63 form =questionnaire_form(data=request.POST) 64 if form.is_valid(): 65 print(form.cleaned_data) 66 print(form.errors) 67 models.Questionnaire.objects.filter(id=questionnaire_id).update(**form.cleaned_data) 68 return redirect('/questionnaire/') 69 else: 70 return render(request, 'edit_questionnaire_obj.html', {'form': form})
3. questionnaire_obj.html
1 <form action="" method="post"> 2 {% csrf_token %} 3 <p>问卷调查名称:{{ form.title }}</p> 4 <p>选择班级:{{ form.cls_id }}</p> 5 <p>选择创建者:{{ form.creator_id }}</p> 6 <input type="submit" value="提交"> 7 </form>
博客系统注册,读完肯定清晰明了,注意这里面有全局钩子的用法,
针对from.error下的__all__
models结构如下
1 class UserInfo(AbstractUser): 2 """ 3 用户信息 4 """ 5 nid = models.AutoField(primary_key=True) 6 nickname = models.CharField(verbose_name='昵称', max_length=32) 7 telephone = models.CharField(max_length=11, null=True, unique=True) 8 avatar = models.FileField(upload_to='avatar/',default="/avatar/default.png") 9 create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) 10 blog = models.OneToOneField(to='Blog', to_field='nid',null=True) 11 12 def __str__(self): 13 return self.username
forms
1 from django import forms 2 from django.forms import widgets 3 4 from . models import UserInfo 5 from django.core.exceptions import NON_FIELD_ERRORS,ValidationError # 手动触发错误 6 7 8 9 class RegForm(forms.Form): # 子类 10 # widget 默认情况下,CharField 具有一个TextInput 在HTML 中生成一个<input type="text"> 11 # attrs 可以对input标签添加属性 12 def __init__(self,request,*args,**kwargs): # 派生方法 13 super().__init__(*args,**kwargs) 14 self.request = request # 派生属性 15 user = forms.CharField(min_length=5,max_length=12,required=True, # 规则 16 error_messages={'min_length':'最小长度5','max_length':'最大长度12','required':'不能为空'}, # 错误信息 17 widget = widgets.TextInput(attrs={'class':'form-control','placeholder':'username'})) # 生成标签,标签属性 18 19 pwd = forms.CharField(required=True,error_messages={'required':'不能为空'},widget = widgets.PasswordInput(attrs={'class':'form-control','placeholder':'password'})) 20 21 repeat_pwd = forms.CharField(required=True,error_messages={'required':'不能为空'},widget = widgets.PasswordInput(attrs={'class':'form-control','placeholder':'repeat_password'})) 22 23 email = forms.EmailField(required=True,error_messages={'invalid':'格式错误','required':'不能为空'}, 24 # 生成一个EmailInput <input type="input"> 25 widget = widgets.EmailInput(attrs={'class':'form-control','placeholder':'email','required':'不能为空'})) 26 27 valid_code = forms.CharField(required=True,error_messages={'required':'不能为空'},widget = widgets.TextInput(attrs={'class':'form-control','placeholder':'valid_code'})) 28 29 # 局部钩子,针对user的验证 30 def clean_user(self): 31 user = UserInfo.objects.filter(username=self.cleaned_data.get('user')) 32 if not user: 33 return self.cleaned_data.get('user') 34 else: 35 raise ValidationError('用户名已经存在') 36 37 # 局部钩子,针对pwd的验证 38 def clean_pwd(self): 39 pwd = self.cleaned_data.get('pwd') 40 if pwd.isdigit() or pwd.isalpha(): 41 raise ValidationError('不能是纯数字或者纯字母') 42 else: 43 return pwd 44 45 # 局部钩子,针对验证码的验证 46 def clean_valid_code(self): 47 val = self.cleaned_data.get('valid_code') 48 if val.upper() == self.request.session.get('valid_code_str').upper(): 49 return val 50 else: 51 raise ValidationError('验证码错误') 52 53 # 全局钩子,针对全局上的验证,注意,先走局部后走全局 54 def clean(self): 55 if self.cleaned_data.get('pwd'): 56 if self.cleaned_data.get('pwd') == self.cleaned_data.get('repeat_pwd'): 57 return self.cleaned_data 58 else: 59 raise ValidationError('俩次密码不一致') 60 61 # 正确信息保存在,clean.data 62 # 错误信息保存在,clean.error
1. urls
1 urlpatterns = [ 2 url(r'^admin/', admin.site.urls), 3 url(r'^login/', views.login_in), 4 url(r'^register/', views.register), 5 ]
2. views
1 from .forms import * 2 def register(request): 3 if request.is_ajax(): 4 # 通过from表单验证 5 print(request.POST) 6 regForm = RegForm(request,request.POST) 7 regResponse = {'user':None,'errors':None} 8 if regForm.is_valid(): 9 # 通过验证,可以注册 10 data = regForm.cleaned_data # 合法的表单数据,是以字典形式保存 11 user = data.get('user') 12 pwd = data.get('pwd') 13 email = data.get('email') 14 avatar_img = request.FILES.get('valid_img') #获取的文件对象 15 print(avatar_img) 16 print(type(avatar_img)) 17 # user_obj = UserInfo.objects.filter(nid=2).update(avatar=avatar_img) 18 user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar_img) 19 regResponse['user'] = user_obj.username 20 else: 21 regResponse['errors'] = regForm.errors # regForm.errors 保存所有错误信息,包括__all__全局钩子错误 22 return JsonResponse(regResponse) 23 24 regForm = RegForm(request) # 前端渲染,虽然没有数据,但是会渲染出标签来 25 return render(request,'register.html',{'regForm':regForm})
3. reg.html
1 <div class="container"> 2 <div class="row"> 3 <div class="col-md-6 col-md-offset-3"> 4 <form> 5 {% csrf_token %} 6 <div class="form-group"> 7 <label for="user">用户名:</label> 8 {# <input type="text" class="form-control" id="user" placeholder="User">#} 9 {{ regForm.user }} <span></span> 10 </div> 11 12 <div class="form-group"> 13 <label for="pwd">密码:</label> 14 {# <input type="password" class="form-control" id="pwd" placeholder="Password">#} 15 {{ regForm.pwd }} <span></span> 16 </div> 17 18 <div class="form-group"> 19 <label for="repeat_pwd">确认密码:</label> 20 {# <input type="password" class="form-control" id="repeat_pwd" placeholder="Repeat Password">#} 21 {{ regForm.repeat_pwd }} <span></span> 22 </div> 23 24 <div class="form-group"> 25 <label for="email">邮箱:</label> 26 {# <input type="email" class="form-control" id="email" placeholder="Email">#} 27 {{ regForm.email }} <span></span> 28 </div> 29 <div class="row"> 30 <div class="col-md-6"> 31 <input type="button" value="confirm register" class="btn btn-primary regBtn"> 32 </div> 33 </div> 34 35 </form> 36 37 </div> 38 </div> 39 </div> 40 41 <script> 42 {# AJAX提交注册表单,注册用户 #} 43 $(".regBtn").click(function () { 44 45 var $formData = new FormData(); 46 47 $formData.append('user',$('#id_user').val()); 48 $formData.append('pwd',$('#id_pwd').val()); 49 $formData.append('repeat_pwd',$('#id_repeat_pwd').val()); 50 $formData.append('email',$('#id_email').val()); 51 $formData.append('valid_code',$('#id_valid_code').val()); 52 var file = $('#avatar_file')[0].files[0]; 53 $formData.append('valid_img',file); 54 $formData.append('csrfmiddlewaretoken',$("[name='csrfmiddlewaretoken']").val()); 55 56 $.ajax({ 57 url: "/register/", 58 type: "POST", 59 data: $formData, 60 processData:false, // 不做转码或预处理 61 contentType:false, // 文件类型不做处理 62 success: function (data) { 63 if (data.user) { 64 location.href = '/login/' 65 } 66 else { 67 $('span').html(''); 68 $(".form-group").removeClass("has-error"); 69 console.log(data.errors); 70 71 $.each(data.errors,function (i,j) { 72 $("#id_" + i).next().addClass('pull-right').css('color', 'red').html(j[0]).parent().addClass('has-error'); 73 if(i == "__all__") { 74 $("#id_repeat_pwd").next().addClass("pull-right").css("color", "red").html(j[0]).parent().addClass("has-error"); 75 $("#id_pwd").parent().addClass("has-error"); 76 } 77 }) 78 } 79 } 80 }) 81 }) 82 </script>