登入,生成隨機圖片驗證碼
一、登入 - 隨機生成圖片驗證碼
1、隨機生成驗證碼
Python隨機生成圖片驗證碼,需要使用PIL模組,安裝方式如下:
pip3 install pillow
1)建立圖片
from PIL import Image img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255)) with open('code.png', 'wb') as f: # 儲存在本地(即寫入硬碟) img.save(f, format='png')
引數說明:
mode='RGB' 表示以RGB來表示顏色
size=(120,30) 表示座標
color=(255, 255, 255) 表示白色
此時,開啟啟動檔案所在目錄,裡面就有了一個寬120,高30的白色code.png圖片。
2)建立畫筆(用於在圖片上畫任意內容)
from PIL import Image, ImageDraw
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255)) draw = ImageDraw.Draw(img, mode='RGB') # 建立畫筆物件draw img.show() # 在圖片檢視器中開啟,這句會呼叫系統預設的圖片管理工具
3)畫點 - point()方法
from PIL import Image, ImageDraw
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255)) draw = ImageDraw.Draw(img, mode='RGB') # point()第一個引數:表示座標, 第二個引數:表示顏色 draw.point([100, 20], fill='red') draw.point([60, 10], fill=(0, 255, 0)) # 儲存在本地 with open('code.png', 'wb') as f: img.save(f, format='png')
效果如下圖:
4)畫線 - line()方法
from PIL import Image, ImageDraw
img = Image.new(mode='RGB', size=(540, 150), color=(255, 255, 255)) draw = ImageDraw.Draw(img, mode='RGB') # line()第一個引數:表示起始座標和結束座標,第二個引數:表示顏色 draw.line((50, 50, 50, 150), fill='red') # 上面一句表示畫一條座標(x=100,y=100)到(x=100,y=300)的直線 draw.line((50, 100, 150, 50), fill=(120, 120, 120)) draw.line((50, 50, 150, 50), fill=(0, 255, 255)) with open('code.png', 'wb') as f: img.save(f, format='png')
效果如下:
5)畫圓 - arc()方法
from PIL import Image, ImageDraw
img = Image.new(mode='RGB', size=(500, 140), color=(255, 255, 255)) draw = ImageDraw.Draw(img, mode='RGB') # 第一個引數:表示起始座標和結束座標(圓要畫在其中間,兩點確定的矩形的內切圓) # 第二個引數:表示開始角度 # 第三個引數:表示結束角度 # 第四個引數:表示顏色 draw.arc((200, 20, 300, 120), 0, 360, fill='red') with open('code.png', 'wb') as f: img.save(f, format='png')
效果如下:
6)寫文字 - text()方法
from PIL import Image, ImageDraw
img = Image.new(mode='RGB', size=(80, 20), color=(255, 255, 255)) draw = ImageDraw.Draw(img, mode='RGB') # 第一個引數:表示起始座標,第二個引數:表示寫入的文字,第三個引數:表示顏色 draw.text([0, 0], 'python', 'red') with open('code.png', 'wb') as f: img.save(f, format='png')
效果如下:
7)特殊字型文字(下載好引用的字型檔案)
from PIL import Image, ImageDraw, ImageFont
img = Image.new(mode='RGB', size=(120, 40), color=(255, 255, 255)) draw = ImageDraw.Draw(img, mode='RGB') font = ImageFont.truetype('kumo.ttf', 28) # 第一個引數:表示字型檔案路徑 # 第二個引數:表示字型大小 draw.text((0, 0), 'python', 'red', font=font) # 第一個引數:表示起始座標 # 第二個引數:表示寫入內容 # 第三個引數:表示顏色 # 第四個引數:表示字型 with open('code.png', 'wb') as f: img.save(f, format='png')
效果如下:
8)隨機生成圖片驗證碼
import random from PIL import Image, ImageDraw, ImageFont, ImageFilter def check_code(width=120, height=30, char_length=5, font_file='../static/font/kumo.ttf', font_size=28): code = [] img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255)) draw = ImageDraw.Draw(img, mode='RGB') def rndChar(): """ 生成隨機字元(包括大小寫字母和數字) :return: """ ranNum = str(random.randint(0, 9)) ranLower = chr(random.randint(65, 90)) ranUpper = chr(random.randint(97, 120)) return random.choice([ranNum, ranLower, ranUpper]) def rndColor(): """ 生成隨機顏色 :return: """ return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255)) # 寫文字 font = ImageFont.truetype(font_file, font_size) for i in range(char_length): char = rndChar() code.append(char) h = ( height - font_size ) / 2 draw.text([i * width / char_length, h], char, font=font, fill=rndColor()) # 寫干擾點 for i in range(40): draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) # 寫干擾圓圈 for i in range(40): draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) x = random.randint(0, width) y = random.randint(0, height) draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor()) # 畫干擾線 for i in range(5): x1 = random.randint(0, width) y1 = random.randint(0, height) x2 = random.randint(0, width) y2 = random.randint(0, height) draw.line((x1, y1, x2, y2), fill=rndColor()) # 對影象加濾波 - 深度邊緣增強濾波 img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) return img, ''.join(code) if __name__ == '__main__': # 1. 直接開啟,即用圖片檢視器檢視 # img,code = check_code() # img.show() # 2. 寫入檔案 # img,code = check_code() # with open('code.png','wb') as f: # f是寫入磁碟的檔案控制程式碼 # img.save(f, format='png') # data = f.read() # data是讀取圖片的位元組 # 3. 寫入記憶體(Python3) # img,code = check_code() # from io import BytesIO # 記憶體管理的模組 # stream = BytesIO() # stream是寫入記憶體的檔案控制程式碼 # img.save(stream, 'png') # data = stream.getvalue() # 4. 寫入記憶體(Python2) # img,code = check_code() # import StringIO # stream = StringIO.StringIO() # stream是寫入記憶體的檔案控制程式碼 # img.save(stream, 'png') # data = stream.getvalue()
效果如下:
2、基於ajax實現登入的示例程式碼
1)urls.py中關於登入程式碼:
path('login/', views.login,), # 獲取登入頁面url path('get_identifyCode/', views.get_identifyCode,), # 獲取驗證碼對應url
2)login.html核心程式碼:
<body> <div id="particles-js"> <div class="login"> <p class="login-top">登入</p> {% csrf_token %} <div class="login-center clearfix"> <label class="" for="user">使用者名稱</label> <input type="text" id="user" placeholder="使用者名稱" /> </div> <div class="login-center clearfix"> <label class="iconfont labelFS" for="pwd">密碼</label> <input type="password" id="pwd" placeholder="密碼" /> </div> <div class="login-center clearfix"> <label class="iconfont labelFS" for="validcode"></label> <input type="text" id="validcode" placeholder="驗證碼" /> <img src="/get_identifyCode/" alt="驗證碼" title="換一張" class="validImg" id="img" width="88" height="30" > </div> <a href="javascript:void(0);" class="login_btn">登入</a> <p class="error"></p> </div> </div> <script src="jquery.min.js"></script> <script> // ajax 登入 $(".login_btn").click(function () { $.ajax({ url:"", type:"post", // data傳送urlencoded格式就行,資料沒那麼深,沒必要發json格式 data:{ user:$("#user").val(), pwd:$("#pwd").val(), validcode:$("#validcode").val(), csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val() }, success:function (response) { console.log(response); if(response.user){ // 登入成功 location.href="/index/" } else{ // 登入失敗 $(".error").html(response.err_msg) } } }) }); // 驗證碼重新整理:img標籤有一個天然的發請求的模式,即src的路徑後邊拼接一個問號就會發一次請求,利用這一原理可以實現驗證碼重新整理 $("#img").click(function () { this.src += "?" }); </script> </body>
3)views.py中獲取隨機驗證碼的檢視函式程式碼(驗證碼儲存利用session)
def get_identifyCode(request): img,code = check_code() # 利用上面的模組得到img物件和驗證碼code f = BytesIO() # 得到寫入記憶體的檔案控制程式碼 img.save(f, "png") # 寫入記憶體 data = f.getvalue() # 從記憶體中讀出 # 將驗證碼存在各自的session中,這樣做的好處是每個人都有自己的驗證碼,不會相互混淆(一定不能設為全域性變數) request.session['keep_str'] = code return HttpResponse(data)
4)views.py中login檢視函式程式碼
from django.contrib import auth def login(request): # if request.method == "POST": if request.is_ajax(): # 判斷是否ajax請求 user = request.POST.get("user") pwd = request.POST.get("pwd") validcode = request.POST.get("validcode") # Ajax請求通常返回一個自己構建的字典 response={"user": None, "err_msg": ""} # request.session.get("keep_str")取出session中驗證碼與使用者輸入作判斷 if validcode.upper() == request.session.get("keep_str").upper(): user_obj = auth.authenticate(username=user, password=pwd) print("user_obj", user_obj, bool(user_obj)) if user_obj: response["user"] = user auth.login(request, user_obj) # 儲存使用者狀態 else: response['err_msg'] = "使用者名稱或者密碼錯誤!" else: response["err_msg"] = "驗證碼錯誤!" return JsonResponse(response) else: return render(request, "login.html")
二、基於ajax和forms元件實現註冊示例
1)model.py
from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): # 將原生auth_user表擴充套件一個tel手機號欄位 tel=models.CharField(max_length=32)
2)forms.py(其實程式碼放在哪裡沒關係,最重要的是程式能找到,為了解耦,我們可以定義一個form.py)
from django import forms # exceptions中存著django的所有錯誤,錯誤在核心元件中 from django.core.exceptions import ValidationError from django.forms import widgets from app01.models import UserInfo class UserForm(forms.Form): # UserForm中定義需要校驗的欄位 username=forms.CharField(min_length=5, label="使用者名稱") password=forms.CharField(min_length=5, widget=widgets.PasswordInput(), label="密碼") r_pwd=forms.CharField(min_length=5, widget=widgets.PasswordInput(), label="確認密碼") email=forms.EmailField(min_length=5, label="郵箱") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields.values(): field.widget.attrs.update({'class': 'form-control'}) # 統一加class def clean_user(self): val=self.cleaned_data.get("username") user=UserInfo.objects.filter(username=val).first() if user: raise ValidationError("使用者已存在!") else: return val def clean_pwd(self): val=self.cleaned_data.get("password") if val.isdigit(): raise ValidationError("密碼不能是純數字!") else: return val def clean_email(self): val = self.cleaned_data.get("email") if re.search("\w+@163.com$", val): return val else: raise ValidationError("郵箱必須是163郵箱!") def clean(self): pwd=self.cleaned_data.get("password") r_pwd=self.cleaned_data.get("r_pwd") if pwd and r_pwd and r_pwd!=pwd: self.add_error("r_pwd", ValidationError("兩次密碼不一致!")) else: return self.cleaned_data
3)reg.html核心程式碼:
<body> <h3>註冊頁面</h3> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method=""> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="">{{ field.label }}</label> {{ field }} <span class="error"></span> </div> {% endfor %} <input type="button" class="btn btn-primary reg_btn" value="註冊"> </form> </div> </div> </div> <script src="jquery.min.js"></script> <script> $(".reg_btn").click(function () { $.ajax({ url:"", type:"post", data:{ username:$("#id_username").val(), password:$("#id_password").val(), r_pwd:$("#id_r_pwd").val(), email:$("#id_email").val(), csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val() }, success:function (res) { if (res.user){ // 註冊成功 location.href="/login/" } else{ // 清除錯誤 $(".error").html(""); // 展示新的錯誤 $.each(res.err_msg,function (i,j) { $("#id_"+i).next().html(j[0]); }) } } }) }) </script> </body>
4)views.py中註冊的檢視函式reg
def reg(request): if request.method == "POST": form = UserInfo(request.POST) res = {"user": None, "err_msg": ""} if form.is_valid(): res["user"] = form.cleaned_data.get("username") del form.cleaned_data["r_pwd"] # 因表中無此欄位,只需校驗,不插入 UserInfo.objects.create_user(**form.cleaned_data) else: res["err_msg"] =form.errors return JsonResponse(res) else: # get請求 form = UserInfoModelForm() return render(request,"reg.html",{"form": form})
三、補充知識點
1、對原生auth_user表擴充套件欄位(使用AbstractUser)
我們之前學習使用者認證元件時,用的是django提供的auth_user表,即通過引入User物件(from django.contrib.auth.models import User)去操作它,我們又發現原始碼中User類繼承了AbstractUser類,所以AbstractUser和User其實就是一張表,所以當我們想要有使用者認證功能,又想要一些auth_user表中沒有的欄位時,可以按照如下這樣做:
在models.py中,引入AbstractUser,並且自己定義一個使用者類(表),這時類中只定義django的auth_user表中沒有而你又想使用的欄位即可,並且讓你定義的類繼承AbstractUser,如下:
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser): tel=models.CharField(max_length=32) # 擴充套件了一個手機號欄位
注意:寫完以上程式碼就直接去遷移資料庫會報錯“HINT: Add or change a related_name argument to the definition for ......”,我們需要在settings.py中加上下面這句話,來告訴Django我們要使用自己定義的表作為使用者認證表(因此登入的使用方法不變,認證時django會自己去找這張表):
AUTH_USER_MODEL="app01.UserInfo"
這時再去進行資料庫遷移,我們發現,資料庫中沒有auth_user表了,而我們自己定義的表中除了有自己定義的那些欄位外,還有之前auth_user表中的所有欄位,這就代表已經達到了我們的目的。
補充:通過命令建立超級使用者的方式:
Tools -- > Run manage.py Task # 執行起來manage.py,再輸入如下命令 manage.py@myproject > createsuperuser # 執行後根據提示輸入使用者名稱,密碼,郵箱 # 注意:輸入的密碼會進行加密處理,再存入表中,並且命令輸入密碼要求最少8位
2、JsonResponse的使用
我們發現,一般瀏覽器傳送Ajax請求給伺服器時,都會返回一個字典,我們需要先將字典序列化,瀏覽器接收到後再進行反序列化,你會不會覺得這樣做有點繁瑣?其實,django為我們提供了一個JsonResponse類,它為我們做好了json的序列化,並且瀏覽器接收到之後,ajax也會自動為我們反序列化,即ajax中success函式接收到的response就是反序列化之後的資料,直接使用即可,如上面登入示例部分程式碼:
from django.http import JsonResponse # 引入JsonResponse def login(request): if request.is_ajax(): ...... response={"user":None, "err_msg": ""} ...... return JsonResponse(response)
分析原因:JsonResponse本質也繼承了HttpResponse,而且既為我們做了序列化的操作,還將資料格式設定為json,ajax收到設定了json格式的資料也會為我們自動反序列化,也說明了不僅僅請求頭中有content-type,響應頭中也有,JsonResponse原始碼如下:
class JsonResponse(HttpResponse): def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None, **kwargs): if safe and not isinstance(data, dict): raise TypeError( 'In order to allow non-dict objects to be serialized set the ' 'safe parameter to False.' ) if json_dumps_params is None: json_dumps_params = {} kwargs.setdefault('content_type', 'application/json') data = json.dumps(data, cls=encoder, **json_dumps_params) super().__init__(content=data, **kwargs)
3、forms元件中對渲染出來的input輸入框統一增加一個類名
我們在學習forms元件時,可以分別給每個欄位設定一個類名,如class="form-control",但發現像之前那樣寫的有程式碼冗餘的問題,按照如下方式寫可以解決此問題:
from django import forms from django.forms import widgets class UserForm(forms.Form): user=forms.CharField(min_length=5, label="使用者名稱") pwd=forms.CharField(min_length=5, widget=widgets.PasswordInput(), label="密碼") r_pwd=forms.CharField(min_length=5, widget=widgets.PasswordInput(), label="確認密碼") email=forms.EmailField(min_length=5, label="郵箱") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for filed in self.fields.values(): filed.widget.attrs.update({'class': 'form-control'})
4、關於全域性鉤子__all__的問題
我們知道全域性鉤子的錯誤資訊都在__all__中,原始碼中是這樣寫的:
所以知道了這些,我們可以自己設定全域性鉤子的欄位,避免跟其他欄位規律不一致造成單獨判斷的問題,如下方式:
# 全域性鉤子:校驗兩次密碼不一致 def clean(self): pwd=self.cleaned_data.get("pwd") r_pwd=self.cleaned_data.get("r_pwd") if pwd and r_pwd and r_pwd!=pwd: self.add_error("r_pwd", ValidationError("兩次密碼不一致!")) # 自己定義錯誤資訊對應的欄位是r_pwd else: return self.cleaned_data
5、關於具有提交功能的按鈕問題
我們知道form表單是瀏覽器向伺服器發請求的一種方式,提交按鈕也有多種,但是要注意,具有提交功能的按鈕有兩種:<input type="submit" value="提交" />和<button>提交</button>,也就是說,當你想用form表單發請求時,可以用以上兩種的任一種,但是當你想基於ajax傳送請求時,若有form標籤,則一定不要用以上兩種提交按鈕,否則當你點選按鈕傳送ajax時會自動以form表單的方式再發一次請求,使用<input type="button" value="提交" />是可以的,因為它沒有提交form表單功能。
原文地址:https://www.cnblogs.com/li-li/p/9911603.html