Django博客項目之登錄和註冊系統
1、配置文件
settings.py文件:
增加一項內容實現UserInfo表繼承Django用戶認證的表 AUTH_USER_MODEL="blog.UserInfo" TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
2、表關系設計
models.py文件內容:
from django.db import models from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): """ 用戶信息 """ nid = models.AutoField(primary_key=True) telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png") create_time = models.DateTimeField(verbose_name='創建時間', auto_now_add=True) blog = models.OneToOneField(to='Blog', to_field='nid', null=True) def __str__(self): return self.username class Blog(models.Model): """ 博客(個人站點)信息 """ nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='個人博客標題', max_length=64) site = models.CharField(verbose_name='個人博客後綴', max_length=32, unique=True) theme = models.CharField(verbose_name='博客主題', max_length=32) def __str__(self): return self.title
3、數據庫實例化
python3 manage.py makemigrations
python3 manage.py migrate
二、登錄系統
urls.py文件內容:
from django.conf.urls import url from django.contrib import admin from blog import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.login), url(r'^valid_img/', views.valid_img), url(r'^log_out/$', views.log_out), url(r'^index/', views.index), ]
views.py文件內容:
from django.shortcuts import render,HttpResponse,redirect import json def login(reqeust): if reqeust.is_ajax(): res={"user":None,"msg":None} user=reqeust.POST.get("user") pwd=reqeust.POST.get("pwd") valid=reqeust.POST.get("valid") print(reqeust.POST) random_str=reqeust.session.get("random_str") #取出session保存的random_str對應的驗證碼字符串 if valid.upper()==random_str.upper(): from django.contrib import auth user=auth.authenticate(username=user,password=pwd) if user: auth.login(request, user) res["user"]=user.username else: res["msg"]="用戶名或者密碼錯誤" else: res["msg"]="驗證碼失敗" #先判斷驗證碼是否正確,正確後接著判斷用戶名和密碼 return HttpResponse(json.dumps(res)) return render(reqeust,"login.html") def valid_img(request): from PIL import Image #需要安裝pillow模塊:pip3 install pillow from PIL import ImageDraw,ImageFont from io import BytesIO image=Image.new("RGB",(250,36),color=get_random_color()) #隨機創建圖片,長度為250px,寬度為36px draw=ImageDraw.Draw(image) font = ImageFont.truetype("blog/static/font/kumo.ttf", size=32) #定義驗證碼的字體格式 random_str="" for i in range(5): #生成一個5位的字符串 random_num=str(random.randint(0,9)) random_low_alpha=chr(random.randint(97,122)) random_up_alpha=chr(random.randint(65,90)) random_char=random.choice([random_num,random_low_alpha,random_up_alpha]) draw.text((35+i*40,0),random_char,get_random_color(),font=font) random_str+=random_char print(random_str) request.session["random_str"]=random_str #得到的驗證碼字符串設置session以用來保存在內存 # 噪點噪線 width=250 height=36 for i in range(10): x1=random.randint(0,width) x2=random.randint(0,width) y1=random.randint(0,height) y2=random.randint(0,height) draw.line((x1,y1,x2,y2),fill=get_random_color()) for i in range(100): draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color()) x = random.randint(0, width) y = random.randint(0, height) draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color()) f=BytesIO() image.save(f,"png") data=f.getvalue() return HttpResponse(data) def index(request): #驗證是不是當前進來的那個用戶,如果用戶已經登錄了就可以看到頁面 # 如果沒有登錄就不讓看見主頁面,就直接返回登錄頁面 if not request.user.is_authenticated(): return redirect("/login/") else: return render(request, "index.html") def log_out(request): auth.logout(request) return redirect("/login/")
login.html文件內容:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bs/css/bootstrap.css"> <script src="/static/js/jquery-3.2.1.min.js"></script> <style> .error{ color: red; margin-left: 20px; } </style> </head> <body> <h3>登錄頁面</h3> <div> <div> <div class="col-md-6 col-md-offset-3"> <form enctype="application/x-www-form-urlencoded"> {% csrf_token %} <div> <label for="">用戶名</label> <input type="text" id="user"> </div> <div> <label for="">密碼</label> <input type="password" id="pwd" > </div> <div> <label for="">驗證碼</label> <div> <div> <input type="text"id="valid" > </div> <div> <img width="250" height="36" src="/valid_img/" alt=""> </div> </div> </div> <input type="button" value="submit" class="login_btn btn btn-default"> <span></span> </form> </div> </div> </div> <script> // 登錄驗證 $(".login_btn").click(function () { $.ajax({ url:"", data:{ user:$("#user").val(), {# 取到用戶輸入的用戶名 #} pwd:$("#pwd").val(), {# 取到用戶輸入的密碼 #} valid:$("#valid").val(), {# 取到用戶輸入的驗證碼 #} csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val() {# 防止出現404錯誤 #} }, type:"post", success:function (data) { console.log(data); var data=JSON.parse(data); {# 接收到的data進行反序列化 #} if(data.user){ location.href = "/index/" {#跳轉到內容頁#} }else{ $(".error").html(data.msg) } } }) }); // 驗證碼局部刷新 $(".valid_img").click(function () { $(this)[0].src+="?" }) </script> </body> </html>
index.html文件內容:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width"> <title>Title</title> </head> <body> <h1>hello{{ request.user.username }}</h1> <button><a href="/log_out/">註銷</a></button> </body> </html>
三、生成驗證碼的幾種方式
1、方式一:這樣的方式吧路徑寫死了,只能是那一張圖片
import os path = os.path.join(settings.BASE_DIR,"static","image","3.jpg") with open(path,"rb") as f: data = f.read() return HttpResponse(data)
2、方式二:每次都顯示不同的圖片,利用pillow模塊,安裝一個pillow模塊,圖片保存到硬盤上
from PIL import Image img = Image.new(mode="RGB",size=(120,40),color="green") #首先自己創建一個圖片,參數size=(120,40) 代表長和高 f = open("validcode.png","wb") #然後把圖片放在一個指定的位置 img.save(f,"png") #保存圖片 f.close() with open("validcode.png","rb") as f: data = f.read() return HttpResponse(data)
3、方式三:我們可以把圖片保存到內存中,完了自動清除,那麽就引入了方式三:利用BytesIO模塊
from io import BytesIO from PIL import Image img = Image.new(mode="RGB",size=(120,40),color="blue") f = BytesIO() #內存文件句柄 img.save(f,"png") #保存文件 data = f.getvalue() #打開文件(相當於python中的f.read()) return HttpResponse(data)
4、方式四:添加畫筆,在圖片上寫上一些文字和噪點噪線
from PIL import Image # 需要安裝pillow模塊:pip3 install pillow from PIL import ImageDraw, ImageFont from io import BytesIO image = Image.new("RGB", (250, 36), color=get_random_color()) # 隨機創建圖片,長度為250px,寬度為36px draw = ImageDraw.Draw(image) font = ImageFont.truetype("blog/static/font/kumo.ttf", size=32) # 定義驗證碼的字體格式 random_str = "" for i in range(5): #生成一個5位的字符串 random_num = str(random.randint(0, 9)) # 隨機取出一個數字 random_low_alpha = chr(random.randint(97, 122)) # 隨機取出一個小寫字母 random_up_alpha = chr(random.randint(65, 90)) # 隨機取出一個大寫字母 random_char = random.choice([random_num, random_low_alpha, random_up_alpha]) # 隨機取出一個小寫字母或者大寫字母或者數字 draw.text((35 + i * 40, 0), random_char, get_random_color(), font=font) # 文字間距是40px,第一個到左邊框距離是35px random_str += random_char # 將取到的字符串內容放到random_str值的後面,每次循環加一個字符串 print(random_str) request.session["random_str"] = random_str # 得到的驗證碼字符串設置session以用來保存在內存 # 噪點噪線 width=250 height=36 for i in range(10): x1=random.randint(0,width) x2=random.randint(0,width) y1=random.randint(0,height) y2=random.randint(0,height) draw.line((x1,y1,x2,y2),fill=get_random_color()) for i in range(100): draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color()) x = random.randint(0, width) y = random.randint(0, height) draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color()) f = BytesIO() image.save(f, "png") data = f.getvalue() return HttpResponse(data)
四、註冊相關內容
1、Form組件
我們一般寫Form的時候都是把它寫在views視圖裏面,還可以在應用下面建一個forms.py的文件來存放
from django.forms import widgets from blog import models from django import forms from django.core.validators import ValidationError class UserForm(forms.Form): user=forms.CharField( required=True, max_length=16, min_length=3, error_messages={ "required": "用戶名不能為空", "max_length": "長度不能大於16", "min_length": "長度不能小於3", }, widget=widgets.TextInput({"placeholder":"用戶名","class":"form-control"})) pwd=forms.CharField( required=True, max_length=16, min_length=3, error_messages={ "required": "密碼不能為空", "max_length": "長度不能大於16", "min_length": "長度不能小於3", }, widget=widgets.PasswordInput({"placeholder": "密碼", "class": "form-control"}) ) tel = forms.CharField( required=True, max_length=11, min_length=11, error_messages={ "required": "手機號碼不能為空", "max_length": "長度必須是11位,請你正確輸入", "min_length": "長度必須是11位,請你正確輸入", }, widget=widgets.TextInput({"placeholder": "手機號碼", "class": "form-control"}) ) email=forms.EmailField( required=True, error_messages={ "required": "郵箱不能為空", "invalid": "郵箱格式有誤" }, widget=widgets.EmailInput({"placeholder": "郵箱", "class": "form-control"}) )
2、局部鉤子函數
# 自定義用戶名驗證: def clean_user(self): user = self.cleaned_data.get("user") valid = models.UserInfo.objects.filter(username=user).first() if valid: raise ValidationError("用戶名已存在") return user # 自定義手機號碼驗證: def clean_tel(self): tel = self.cleaned_data.get("tel") if len(tel) == 11: return tel else: raise ValidationError("手機號碼格式錯誤")
3、全局鉤子函數
# 自定義密碼驗證: def clean_pwd(self): password = self.cleaned_data.get("pwd") if password.isdigit(): raise ValidationError("密碼需要數字字母組合") else: return password
4、css中的三種隱藏:
(1)display:none #隱藏所有內容
(2)visibility:hidden #隱藏內容
(3)overflow:hidden #隱藏溢出內容
三者都是用來隱藏的:
區別在於:
visibility雖然隱藏了,但是被隱藏的內容依然占據這空間,這段隱藏了的內容卻保留空間的位置會在網頁中顯示空白,而display:隱藏了不占用空間
5、 jQuery的屬性操作相關的
attr: 一個參數是獲取屬性的值,兩個參數是設置屬性值 removeAttr(屬性名): 刪除屬性值 prop: 適應於屬性的返回值是布爾類型的(單選,反選,取消的例子) removePorp: 刪除屬性的值
6、提交二進制數據用FormData
$(".reg_btn").click(function () { var formdata=new FormData(); formdata.append("user",$("#user").val()); formdata.append("pwd",$("#pwd").val()); formdata.append("email",$("#email").val()); formdata.append("tel",$("#tel").val()); formdata.append("repeat_pwd",$("#repeat_pwd").val()); formdata.append("avatar_img",$("#avatar")[0].files[0]); $.ajax({ url:"", type:"post", contentType:false, processData:false, data:formdata, {#ajax上傳有文件用到formdata#} success:function (data) { // console.log(data); var data=JSON.parse(data); {#反序列化json#} if (data.user){ console.log("OK") }else{ $("form span.error").html("") $.each(data.msg,function (filed,error_list) { $("#"+filed).next().html(error_list[0]) }) } } }) })
7、上傳文件有一個固定的配置參數media
(1)首先在settings中配置:
MEDIA_URL="/media/" #別名
MEDIA_ROOT=os.path.join(BASE_DIR,"app01","media","uploads") #具體路徑
(2)在url中配置:
url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
(3)作用
avatar = models.FileField(verbose_name='頭像', upload_to='avatar', default="/avatar/default.png")
會把接收的文件放在media指代的路徑與upload_to的拼接:BASE_DIR+blog+media+uploads+avatar/a.png
avatar字段在數據庫中保存的是:avatar/a.png
<img src="/media/avatar/a.png">
(4)文件保存的位置:app01項目下------->media目錄下------->uploads目錄下------->avatar目錄下
五、用戶註冊系統
views.py文件內容:
from django.shortcuts import render,HttpResponse from django.forms import widgets from blog import models from django import forms import json from django.core.validators import ValidationError class UserForm(forms.Form): user=forms.CharField( required=True, max_length=16, min_length=3, error_messages={ "required": "用戶名不能為空", "max_length": "長度不能大於16", "min_length": "長度不能小於3", }, widget=widgets.TextInput({"placeholder":"用戶名","class":"form-control"})) pwd=forms.CharField( required=True, max_length=16, min_length=3, error_messages={ "required": "密碼不能為空", "max_length": "長度不能大於16", "min_length": "長度不能小於3", }, widget=widgets.PasswordInput({"placeholder": "密碼", "class": "form-control"}) ) repeat_pwd=forms.CharField( required=True, max_length=16, min_length=3, error_messages={ "required": "密碼不能為空", "max_length": "長度不能大於16", "min_length": "長度不能小於3", }, widget=widgets.PasswordInput({"placeholder": "確認密碼", "class": "form-control"}) ) tel = forms.CharField( required=True, max_length=11, min_length=11, error_messages={ "required": "手機號碼不能為空", "max_length": "長度必須是11位,請你正確輸入", "min_length": "長度必須是11位,請你正確輸入", }, widget=widgets.TextInput({"placeholder": "手機號碼", "class": "form-control"}) ) email=forms.EmailField( required=True, error_messages={ "required": "郵箱不能為空", "invalid": "郵箱格式有誤" }, widget=widgets.EmailInput({"placeholder": "郵箱", "class": "form-control"}) ) #自定義用戶名驗證:局部鉤子 def clean_user(self): user = self.cleaned_data.get("user") valid = models.UserInfo.objects.filter(username = user).first() if valid: raise ValidationError("用戶名已存在") return user #自定義手機號碼驗證: def clean_tel(self): tel = self.cleaned_data.get("tel") if len(tel)== 11: return tel else: raise ValidationError("手機號碼格式錯誤") #自定義密碼驗證: def clean_pwd(self): password = self.cleaned_data.get("pwd") if password.isdigit(): raise ValidationError("密碼需要數字字母組合") else: return password #自定義全局鉤子:驗證兩次密碼是否一致 def clean(self): if self.cleaned_data.get("pwd") == self.cleaned_data.get("repeat_pwd"): return self.cleaned_data else: raise ValidationError("兩次密碼不一致") def reg(request): if request.method=="POST": print(request.POST) print(request.FILES) form=UserForm(request.POST) res={"user":None,"msg":None} if form.is_valid(): username=form.cleaned_data.get("user") password=form.cleaned_data.get("pwd") email=form.cleaned_data.get("email") telephone=form.cleaned_data.get("tel") avatar_img = request.FILES.get("avatar_img") print(username,password,telephone) models.UserInfo.objects.create_user(user=username, pwd=password, email=email, avatar=avatar_img) else: res["msg"]=form.errors return HttpResponse(json.dumps(res)) return render(request,"reg.html")
reg.html文件內容:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bs/css/bootstrap.css"> <script src="/static/js/jquery-3.2.1.min.js"></script> <style> #avatar{ display: none; {#隱藏input文件選擇框#} } .avatar_img{ width: 60px; height: 60px; margin-left: 10px; } .error{ color: red; } </style> </head> <body> <h3>註冊頁面</h3> <div> <div> <div class="col-md-6 col-md-offset-3"> <form action="/reg/" method="post" novalidate enctype="multipart/form-data"> {% csrf_token %} <div> <label for="user">用戶名</label> <input type="text" id="user"><span class="error pull-right"></span> </div> <div> <label for="pwd">密碼</label> <input type="password" id="pwd" ><span class="error pull-right"></span> </div> <div> <label for="repeat_pwd">確認密碼</label> <input type="password" id="repeat_pwd" ><span class="error pull-right"></span> </div> <div> <label for="email">手機</label> <input type="email" id="tel" ><span class="error pull-right"></span> </div> <div> <label for="email">郵箱</label> <input type="email" id="email" ><span class="error pull-right"></span> </div> <div> <label for="avatar">頭像 <img src="/static/img/default.png" alt=""></label> <input type="file" id="avatar" > </div> <input type="button" value="submit" class="reg_btn btn btn-default"> <span></span> </form> </div> </div> </div> <script> // 頭像預覽 $("#avatar").change(function () { {#綁定change事件#} var reader=new FileReader(); {#文件閱讀器實例化對象 #} var choose_file=$(this)[0].files[0]; {#取到用戶選中的文件對象#} reader.readAsDataURL(choose_file); {#拿到文件url路徑#} reader.onload=function(){ {#等reader閱讀器加載完後才執行裏面的代碼#} $(".avatar_img").attr("src",reader.result) }; }) // 註冊 $(".reg_btn").click(function () { var formdata=new FormData(); formdata.append("user",$("#user").val()); formdata.append("pwd",$("#pwd").val()); formdata.append("email",$("#email").val()); formdata.append("tel",$("#tel").val()); formdata.append("repeat_pwd",$("#repeat_pwd").val()); formdata.append("avatar_img",$("#avatar")[0].files[0]); $.ajax({ url:"", type:"post", contentType:false, processData:false, data:formdata, {#ajax上傳有文件用到formdata#} success:function (data) { // console.log(data); var data=JSON.parse(data); {#反序列化json#} if (data.user){ location.href = "/login/" }else{ // 清空操作 $("form span.error").html("") console.log(data.msg) $.each(data.msg,function (filed,error_list) { {#循環data.msg,其中filed是鍵,對應的值是一個字典類型的數據#} $span = $("<span>"); $span.addClass("pull-right").css("color","red"); $span.html(error_list[0]); $("#"+filed).next().html(error_list[0]) {# "#"+filed拼接標簽,然後在下一個span標簽填充取到的錯誤信息#} if (filed=="__all__"){ {#filed=="__all__"表示全局鉤子取到了錯誤信息 #} $("#repeat_pwd").next().html($span) } }) } } }) }) </script> </body> </html>
Django博客項目之登錄和註冊系統