1.Form表单的input中的type=button和type=submit是有区别的:
Submit会直接提交表单数据,发送post请求
Button只是一个普通按钮
2.当用户认证组件中的字段不够我们使用时,可以自定义其他的字段
1 from django.contrib.auth.models import AbstractUser 2 class UserInfo(AbstractUser): 3 tel=models.CharField(max_length=32)
同时要配置settings文件:告诉Django哪个app下的哪个表
AUTH_USER_MODEL="app01.UserInfo"
此时原生的user表就变成了userinfo表
3.如何给注册登录页面添加验证码
图片的处理要基于PIL模块,下载PIL模块,要下载包:pip3 install pillow
在urls.py中
1 from django.contrib import admin 2 from django.urls import path 3 from app01 import views 4 urlpatterns = [ 5 path('admin/', admin.site.urls), 6 path('reg/', views.reg), 7 path('login/', views.login), 8 path('index/', views.index), 9 path('get_valid_img/', views.get_valid_img), 10 ]
在models.py中
1 from django.contrib.auth.models import AbstractUser 2 class UserInfo(AbstractUser): 3 email=models.CharField(max_length=32) 4 5 #向authform组件中的user表中增加其他字段,字段增加后的表名叫app01_userinfo
用户认证组件
1 class UserForm(forms.Form): 2 user=forms.CharField(min_length=5,label="用户名") 3 pwd=forms.CharField(min_length=5,label="密码") 4 r_pwd=forms.CharField(min_length=5,label='确认密码') 5 email=forms.EmailField(min_length=5,label="邮箱")
视图中的注册函数
1 def reg(request): 2 if request.method=="GET": 3 form=UserForm() 4 return render(request,"reg.html",locals()) 5 else: 6 form=UserForm(request.POST) 7 response_dic={"islogin":None} 8 if form.is_valid(): 9 user=request.POST.get("user") 10 pwd=request.POST.get("pwd") 11 email=request.POST.get("email") 12 UserInfo.objects.create_user(username=user,password=pwd,email=email) 13 response_dic["islogin"]=True 14 else: 15 response_dic["islogin"] = False 16 error=form.errors 17 response_dic["error"] = error 18 return HttpResponse(json.dumps(response_dic))
注册HTML中
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"> 7 </head> 8 <body> 9 <div class="container"> 10 <div class="row"> 11 <div class="col-md-8 col-md-offset-2"> 12 <h3>注册页面</h3> 13 <form action=""> 14 {% csrf_token %} 15 {% for field in form %} 16 <label for="">{{ field.label }}</label> 17 {{ field }} <p class="error"></p> 18 {% endfor %} 19 <input type="button" class="btn btn-success login_btn" value="提交"> 20 </form> 21 </div> 22 </div> 23 </div> 24 <script src="/static/js/jquery.js"></script> 25 <script> 26 $(".login_btn").click(function(){ 27 $.ajax({ 28 url:"", 29 type:"post", 30 data:{ 31 "user":$('#id_user').val(), 32 "pwd":$("#id_pwd").val(), 33 "r_pwd":$("#id_r_pwd").val(), 34 "email":$("#id_email").val(), 35 "csrfmiddlewaretoken":$("[name=csrfmiddlewaretoken]").val(), 36 }, 37 success:function(response){ 38 response.is_login==true 39 location.href="/login/" 40 } 41 }) 42 }) 43 </script> 44 </body> 45 </html>
登录视图需要用到的验证码视图
1 def get_valid_img(request): 2 # 方式一:读取指定的图片 3 # with open("static/aaa.jpg","rb") as f: 4 # data=f.read() 5 6 # 方式二:基于PIL模块,创建验证码图片 7 # from PIL import Image 8 # 1.创建图片 9 # img=Image.new("RGB",(350,34),"yellow") 10 # 原码部分:三个参数分别是模式,图片大小,背景色 11 # def new(mode, size, color=0): 12 # 颜色参数动态可以使用三原色 13 # def get_do_color(): 14 # import random 15 # return (random.randint(0,255),random.randint(0,255),random.randint(0,255)) 16 # img=Image.new("RGB",(350,34),get_do_color()) 17 18 19 # 2.生成图片 20 # f=open("valid.png","wb") 21 # img.save(f,"png") 22 # 原码部分:fp是文件句柄 最后一个参数是文件格式,文件句柄就是文件存储的位置 23 # def save(self, fp, format=None, **params): 24 # with open("valid.png","rb") as f: 25 # data=f.read() 26 # 每次都要放到磁盘中太浪费空间 27 28 # 方式三:将图片存到内存中,对内存的控制需要io模块 29 # from io import BytesIO 30 # from PIL import Image 31 # def get_do_color(): 32 # import random 33 # return (random.randint(0,255),random.randint(0,255),random.randint(0,255)) 34 # img=Image.new("RGB",(350,34),get_do_color()) 35 # f=BytesIO() 36 # 内存句柄,将文件存到内存中 37 # img.save(f,"png") 38 # data=f.getvalue() 39 # 从内存中读取文件 40 41 42 # 方式四:完善文本 43 # from io import BytesIO 44 # from PIL import Image ,ImageFont,ImageDraw #为图片添加内容需要引入画笔,ImageFont为创建字体对象 45 # def get_do_color(): 46 # import random 47 # return (random.randint(0,255),random.randint(0,255),random.randint(0,255)) 48 # img=Image.new("RGB",(350,34),get_do_color()) 49 # draw=ImageDraw.Draw(img) #画笔需要一个参数作为画板 50 # 构建字体对象 51 # font=ImageFont.truetype("static/font/kumo.ttf",40) 52 # draw.text((0,0),"aaaaaa",fill=get_do_color(),font=font) 53 # text方法原码部分 54 # def text(self, xy, text, fill=None, font=None, anchor=None, *args, **kwargs): 55 # 画笔的text方法 xy参数为一个坐标元组,表示画在背景的哪里,text为需要画的内容,fill是画笔的颜色 56 # font代表字体 57 58 # 以下是图片的生成,文本部分添加在上面 59 # f=BytesIO() 60 # img.save(f,"png") 61 # data=f.getvalue() 62 # return HttpResponse(data) 63 64 # 方式五 65 import random 66 from io import BytesIO 67 from PIL import Image, ImageFont, ImageDraw 68 def get_do_color(): 69 70 return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) 71 72 img = Image.new("RGB", (350, 34), get_do_color()) 73 draw = ImageDraw.Draw(img) 74 font = ImageFont.truetype("static/font/kumo.ttf", 40) 75 # 动态画背景上的内容 76 77 content="" 78 for i in range(6): 79 random_num=str(random.randint(0,9)) 80 random_lower=chr(random.randint(97,122)) 81 random_upper=chr(random.randint(65,90)) 82 s=random.choice([random_num,random_lower,random_upper]) 83 i+=1 84 draw.text((20+i*40,0), s, fill=get_do_color(), font=font) 85 content=content+str(s) 86 del request.session["content"] 87 request.session["content"]=content 88 89 # 加噪点噪线,让人能识别出来,机器识别不出来 90 width = 350 91 height = 34 92 for i in range(5): 93 x1 = random.randint(0, width) 94 x2 = random.randint(0, width) 95 y1 = random.randint(0, height) 96 y2 = random.randint(0, height) 97 draw.line((x1,y1,x2,y2),fill=get_do_color()) 98 for i in range(20): 99 draw.point([random.randint(0,width),random.randint(0,height)],fill=get_do_color()) 100 x=random.randint(0,width) 101 y=random.randint(0,height) 102 draw.arc((x,y,x+4,y+4),0,90,fill=get_do_color()) 103 104 f = BytesIO() 105 img.save(f, "png") 106 data = f.getvalue() 107 return HttpResponse(data)
登录视图
1 def login(request): 2 if request.method=="GET": 3 return render(request,"login.html") 4 else: 5 user=request.POST.get("user") 6 pwd=request.POST.get("pwd") 7 yzm=request.POST.get("yzm") 8 response={"user":None,"error_msg":""} 9 if str(yzm).upper()==str(request.session.get("content")).upper(): 10 user_obj=auth.authenticate(username=user,password=pwd) 11 if user_obj: 12 response["user"]=user 13 else: 14 response["error_msg"] = "用户名或密码错误" 15 else: 16 response["error_msg"]="验证码错误" 17 return HttpResponse(json.dumps(response))
登录的HTML
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>登录</title> 6 <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"> 7 <script src="/static/js/jquery.js"></script> 8 </head> 9 <body style="background-image: url('https://ss0.bdstatic.com/k4oZeXSm1A5BphGlnYG/skin/831.jpg?2')"> 10 <div class="container" > 11 <dic class="row"> 12 <div class="col-md-offset-2 col-md-8"> 13 <form action=""> 14 {% csrf_token %} 15 <div class="form-group"> 16 <label for="">用户名</label> 17 <input type="text" class="form-control" id="user"> 18 </div> 19 20 <div class="form-group"> 21 <label for="">密码</label> 22 <input type="password" class="form-control" id="pwd"> 23 </div> 24 25 <div class="form-group"> 26 <label for="">验证码</label> 27 <div class="row"> 28 <div class="col-md-6 col-sm-6 col-lg-6"> 29 <input type="text" class="form-control" id="yzm"> 30 </div> 31 <div class="col-md-6 col-sm-6 col-lg-6"> 32 <img width="350" height="34" src="/get_valid_img/" alt=""> 33 </div> 34 </div> 35 </div> 36 <p class="error"></p> 37 <input type="button" value="提交" class="pull-right btn btn-success login_btn"> 38 </form> 39 </div> 40 </dic> 41 </div> 42 43 <script> 44 $(".login_btn").click(function(){ 45 $.ajax({ 46 url:"", 47 type:"post", 48 data:{ 49 "user":$("#user").val(), 50 "pwd":$("#pwd").val(), 51 "yzm":$("#yzm").val(), 52 "csrfmiddlewaretoken":$("[name=csrfmiddlewaretoken]").val() 53 }, 54 success:function(response){ 55 dic=JSON.parse(response); 56 if(dic.user){ 57 location.href="/index/"; 58 } 59 else{ 60 $(".error").val(dic.erroe_msg); 61 } 62 } 63 64 }) 65 }) 66 </script> 67 </body> 68 </html>
- 在视图函数中,除了可以用request.method做分支的判断外,还可以用request.is_ajax()
- JsonResponse
引入:from django.http import JsonResponse
使用:return JsonResponse(dic)
作用:视图函数中不用每一次向前端传字典都Json了,前端通过ajax得到的response不用parse了
原因:查看源码
1 class JsonResponse(HttpResponse): 2 def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, 3 json_dumps_params=None, **kwargs): 4 if safe and not isinstance(data, dict): 5 raise TypeError( 6 'In order to allow non-dict objects to be serialized set the ' 7 'safe parameter to False.' 8 ) 9 if json_dumps_params is None: 10 json_dumps_params = {} 11 kwargs.setdefault('content_type', 'application/json')
//原码中修改了响应头中的content_type,告诉浏览器以json的形式解码
data = json.dumps(data, cls=encoder, **json_dumps_params)
//原码中帮我们做了序列化的工作
super().__init__(content=data, **kwargs)
//并且传到了HttpResponse中
Ajax的success在接收到response时,会先看响应头中有没有 content_type,如果有,按照格式解码,如果没有,直接放到response中
- 验证码的点击刷新
Img标签有一个独特的刷新方式,在路径(http://127.0.0.1:8000/get_valid_img/)后加问号就是获取新的图片,问号可以不止一个,所以验证码的点击刷新即为img标签绑定一个click事件
$("#img").click(function(){
$(this)[0].src+="?"
})
- 笔记本的任务管理器可以查看,终止正在运行的程序
- Forms组件补充
如果组件中的每一个字段都有同样的约束,可以给该组件添加一个__init__ 方法,让其在继承父类__init__方法的同时,填加新的内容
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
for filed in self.fields.values():
filed.widget.attrs.update({"class":"form-control"})
- 全局错误可以不用放到__all__中
正常情况下的全局钩:会将全局错误放到__all__中
1 Def clean(self): 2 3 Pwd=seif.cleaned_data.get(“pwd”) 4 5 R_pwd=eif.cleaned_data(“r_pwd”) 6 7 If pew=r_pwd: 8 9 Return self.cleaned_data 10 11 Else: 12 13 Raise ValidationError(“两次密码不一致”) 14 15 让全局错误放到其他字段中: 16 17 Def clean(self): 18 19 Pwd=seif.cleaned_data.get(“pwd”) 20 21 R_pwd=eif.cleaned_data(“r_pwd”) 22 23 If pew=r_pwd: 24 25 Return self.cleaned_data 26 27 Else: 28 29 Self.add_error(“r_pwd”, ValidationError(“两次密码不一致”))