上周內容
bbs項目
項目開發流程
需求分析
架構設計
分組開發
我們一般情況下都只是作用於這一步
各項測試
交付上線
bbs表設計
任何一個項目 最最重要的部分都是數據庫的表設計
用戶表
繼承AbstractUser表 來做額外的字段擴展
1.類的繼承
2.配置文件配置
AUTH_USER_MODEL = '應用名.類名'
phone
avatar
upload_to='avatar/'
register_time
個人站點表
站點標題
站點名稱
站點樣式
模擬
文章標簽表
標簽名稱
文章分類表
分類名稱
文章表
文章標題
文章簡介
文章內容
文章日期
# 數據庫優化字段
點贊數
點踩數
評論數
點贊點踩表
用戶id 一對多字段
文章id 一對多字段
點贊點踩 1/0
評論表
用戶id 一對多字段
文章id 一對多字段
評論內容
評論時間
自關聯字段 自己跟自己所在的表關聯
數據庫配置
1.配置文件
2.__init__文件
數據庫遷移命令
python3 manage.py makemigrations
python3 manage.py migrate
注冊功能
只要是注冊功能一般都有一定的校驗規則
我們利用forms組件 來完成注冊的校驗
用戶名
label
error_messages
required
invalid
widget
validators
密碼
確認密碼
郵箱
鈎子函數
校驗用戶名是否已存在
校驗兩次密碼是否一致
一旦你的項目特別龐大 需要用到很多forms組件
那么你可以單獨的將所有forms組件放到一個py文件中或者一個文件下
然后針對不同的功能開設不同的py文件
單獨的py文件
文件夾
1.py
2.py
3.py
4.py
注冊功能使用的是ajax做后端交互
但是我們用的了form標簽
因為form標簽有一個自動序列化普通鍵值對的功能
可以方便的將普通鍵值對循環添加到formdata對象中
label標簽在跟input有綁定關系的前提下
內部無論放什么對象 點擊都能夠是對應的input框聚焦
img標簽src屬性可以放的值
1.圖片的地址
2.圖片的二進制數據
3.后端url 自動朝該url發送get請求
實時展示用戶選擇的頭像
利用內置對象fileReader
文件閱讀器在讀取文件的時候 是一個異步io操作
所以你在渲染img標簽的時候一定要等待文件閱讀器加載完畢之后再渲染
等待...加載完畢
onload
window.onload = function(){}
fileReader.onload = function(){}
發送ajax請求
1.ajax提交數據基本語法
2.ajax發送文件需要借助於內置對象formdata
ajax發送formdata對象
需要制定兩個參數都為false
processData
contentType
后端正常的業務邏輯無需多說
一旦報錯之后前端如何對應的渲染相應的信息
1.你需要研究form組件渲染的input框的id值的特點
id_字段名
2.你又發現返回的報錯信息 鍵就是一個個的字段名
自己手動拼接處input框的id值
然后利用DOM操作 來操作span標簽以及div標簽
最后為了功能的健壯性
給所有的input設置獲取焦點事件
自動移除報錯信息和報錯的樣式
控制字體樣式的文件都是以.ttf結尾
本周
bbs結束
今日內容
登錄功能
首頁搭建
導航條用戶功能
admin后台管理
下周安排(15d~22d)
vue前端框架 3~5d
django restframework(******)
路飛項目
git
celery
項目上線
今日內容
url.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 注冊功能
url(r'^register/',views.register,name='register'),
# 登錄功能
url(r'^login/',views.login,name='login'),
# 驗證碼圖片
url(r'^get_code/',views.get_code,name='code'),
# 首頁搭建
url(r'^home/',views.home,name='home'),
# 退出登錄
url(r'^logout/',views.logout,name='logout'),
# 修改密碼
url(r'^set_password/',views.set_password,name='set_pwd')
]
views.py
from django.shortcuts import render,HttpResponse,redirect,reverse
from app01 import myform
from app01 import models
from django.http import JsonResponse
# 驗證碼相關
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO, StringIO
# auth模塊校驗密碼
from django.contrib import auth
from django.contrib.auth.decorators import login_required
# Create your views here.
def register(request):
# 生成一個空的forms對象
form_obj = myform.MyRegForm()
if request.method == 'POST':
back_dic = {'code':1000,'msg':''}
# 對用戶提交的數據先進行校驗 forms
# 直接使用forms組件的校驗方法,校驗整個的數據字典
form_obj = myform.MyRegForm(request.POST)
if form_obj.is_valid():
# 如果數據校驗結果正確
clean_data = form_obj.cleaned_data # 獲取正確的數據 4個鍵值對
# 將確認密碼的鍵值對彈出,只剩余三個便於創建用戶
clean_data.pop('confirm_password')
# 獲取用戶上傳的文件
file_obj = request.FILES.get('avatar')
# 判斷文件是否存在,用戶是否上傳,如果沒有上傳才會設置default頭像
if file_obj:
clean_data['avatar'] = file_obj # 四個鍵值對
# 自動創建,使用**直接打散關鍵字參數
models.Userinfo.objects.create_user(**clean_data)
# 添加成功界面信息,及跳轉
back_dic['msg'] = '注冊成功'
back_dic['url'] = '/login/'
else:
back_dic['code'] = 2000
# 將錯誤信息返回給前端
back_dic['msg'] = form_obj.errors
# 返回給前端字典
return JsonResponse(back_dic)
return render(request,'register.html',locals())
# 登錄功能
def login(request):
if request.method == 'POST':
back_dic = {'code':1000,'msg':''}
username = request.POST.get('username')
password = request.POST.get('password')
code = request.POST.get('code')
# 1.先校驗驗證碼是否正確 忽略大小寫
if request.session.get('code').upper() == code.upper():
# 2.校驗用戶名密碼是否正確 利用auth模塊
user_obj = auth.authenticate(request,username=username,password=password)
if user_obj:
# 3.保存用戶登錄狀態
auth.login(request,user_obj)
# 就可以在任意位置通過request.user獲取到當前登錄對象 並且 request.user.is_authenticated()判斷當前用戶是否登錄
# 返回前端信息,並定向主頁
back_dic['msg'] = '登錄成功'
back_dic['url'] = '/home/'
else:
back_dic['code'] = 2000
back_dic['msg'] = '用戶名或密碼錯誤'
else:
back_dic['code'] = 3000
back_dic['msg'] = '驗證錯誤'
# 返回給前端字典數據
return JsonResponse(back_dic)
return render(request,'login.html')
import random
# 給驗證碼的圖片添加隨機顏色
def get_random():
return random.randint(0,255),random.randint(0,255),random.randint(0,255)
# 圖片驗證碼相關
def get_code(request):
# 推導步驟一:直接發送后端存在的圖片
# img標簽可以直接返回二進制數據進行顯示
# with open(r'static/img/02b02.jpg.jpg','rb') as f:
# data = f.read()
# return HttpResponse(data)
# 推導步驟二 利用pillow模塊自動生成圖片
'''
from PIL import Image,ImageDraw,ImageFont
Image 生成圖片
ImageDraw 在圖片上寫字
ImageFont 控制字體的樣式'''
# 生成圖片對象
# img_obj = Image.new('RGB',(360,35),'red') 直接放文件的顏色
# img_obj = Image.new('RGB',(360,35),get_random()) # 放rgb模式(255,23,232)
# # 利用文件操作先保存下來
# with open('xxx.png','wb') as f:
# img_obj.save(f,'png')
# # 然后利用文件操作將圖片數據讀取並發送
# with open(r'xxx.png','rb') as f:
# data = f.read()
# return HttpResponse(data)
# 推導步驟三 臨時存儲數據並且能夠隨時取出的地方
'''內存管理模塊
from io import BytesIO,StringIO
BytesIO 保存數據(並且在獲取的時候,是以二進制的方式給你)
StringIO 保存數據(以字符串的形式給你)
'''
# img_obj = Image.new('RGB',(360,35),get_random())
# # # 先生成一個io對象
# # io_obj = BytesIO() # 可以將對象當做是文件的句柄
# # # 存儲數據
# # img_obj.save(io_obj,'png')
# # # getvalue獲取二進制的數據
# # return HttpResponse(io_obj.getvalue())
# 推導步驟四 在圖片上寫字
img_obj = Image.new('RGB',(360,35),get_random())
# 將生成好的圖片對象交給imageDraw
img_draw = ImageDraw.Draw(img_obj) # 生成一個畫筆對象
# 字體樣式,將ttf格式字體,及字體的大小 設置
img_font = ImageFont.truetype('static/fonts/1.ttf',30)
# 產生一個隨機驗證碼 大小寫英文加數字 五位,每一位都可以是大小寫數據
code= ''
for i in range(5):
# chr 根據數字對應字符編碼的英文
upper_str = chr(random.randint(65,90))
lower_str = chr(random.randint(97,122))
random_int = str(random.randint(0,9))
# 隨機選取一個字符
tmp = random.choice([upper_str,random_int,lower_str])
# 向圖片中寫入一個((x軸,y軸)坐標,文本,圖片北京,字體樣式)
img_draw.text((i*60+60,0),tmp,get_random(),img_font)
# 存儲寫的字
code += tmp
print(code)
# 這個驗證碼后面的其他視圖函可能用得上,找到地方保存,並且這個地方的全局視圖函數都可以訪問
request.session['code'] = code
# 圖片保存
io_obj = BytesIO()
img_obj.save(io_obj,'png')
return HttpResponse(io_obj.getvalue())
# 首頁的搭建
def home(request):
return render(request,'home.html',locals())
# 退出登錄
@login_required
def logout(request):
# 退出登錄
auth.logout(request)
# 重定向到主頁
return redirect(reverse('home'))
@login_required
def set_password(request):
if request.method == 'POST':
old_pwd = request.POST.get('old_password')
new_pwd = request.POST.get('new_password')
confirm_password = request.POST.get('confirm_password')
# 1.先判斷舊密碼是否正確
is_right = request.user.check_password(old_pwd)
if is_right:
# 判斷新密碼與確認密碼是否一致
if new_pwd == confirm_password:
# 修改密碼
request.user.set_password(new_pwd)
request.user.save()
# 重定向界面
return redirect(reverse('login'))
else:
return HttpResponse('兩次密碼不一致')
else:
return HttpResponse('原密碼不正確')
login
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登錄頁面</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2 ">
<h2 class="text-center">登錄界面</h2>
<div class="form-group">
<label for="id_username">用戶名</label>
<input type="text" name="username" id="id_username" class="form-control">
</div>
<div class="form-group">
<label for="id_password">密碼</label>
<input type="password" name="password" id="id_password" class="form-control">
</div>
{# 驗證碼框#}
<div class="form-group">
<label for="id_code">驗證碼</label>
<div class="row">
<div class="col-md-6">
<input type="text" name="code" id="id_code" class="form-control">
</div>
<div class="col-md-6">
<img src="/get_code/" alt="" width="360" height="35" id="id_img">
</div>
</div>
</div>
<input type="button" class="btn btn-primary" value="登錄" id="id_submit">
{# // 錯誤信息的展示#}
<span style="color: red;" id="id_error"></span>
</div>
</div>
</div>
<script>
{#綁定驗證碼點擊刷新事件#}
$('#id_img').click(function () {
var oldPath = $(this).attr('src');
$(this).attr('src',oldPath+='?')
});
{#發送ajax請求與后端交互#}
$('#id_submit').click(function () {
$.ajax({
url:'',
type:'post',
data:{
'username':$('#id_username').val(),
'password':$('#id_password').val(),
'code':$('#id_code').val(),
'csrfmiddlewaretoken':'{{ csrf_token }}'
},
{#進行處理后端數據#}
success:function (data) {
if(data.code==1000){
// 驗證成功則跳轉主頁
window.location.href = data.url
}else { // 報錯信息的展示
$('#id_error').text(data.msg)
}
}
})
})
</script>
</body>
</html>
home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>主頁</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
{#導航條樣式#}
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/home/">BBS首頁系統</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">文章 <span class="sr-only">(current)</span></a></li>
<li><a href="#">隨筆</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">更多 <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
{# 判斷用戶是否登錄 顯示登錄注冊按鈕#}
{% if request.user.is_authenticated %}
{# 根據后端,顯示登錄之后的按鈕#}
<li><a href="#">{{ request.user.username }}</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">更多操作 <span class="caret"></span></a>
<ul class="dropdown-menu">
{# 添加模態框的屬性data-toggle="modal" data-target="#myModal"#}
<li><a data-toggle="modal" data-target="#myModal">修改密碼</a></li>
<li><a href="#">修改頭像</a></li>
<li><a href="#">后台管理</a></li>
<li role="separator" class="divider"></li>
<li><a href="{% url 'logout' %}">退出登錄</a></li>
</ul>
</li>
{% else %}
{# 沒有登錄則顯示登錄注冊按鈕#}
<li><a href="{% url 'login' %}">登錄</a></li>
<li><a href="{% url 'register' %}">注冊</a></li>
{% endif %}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{#整體頁面布局#}
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
{# 左邊面板#}
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">點擊就送</h3>
</div>
<div class="panel-body">
勞斯萊斯5元代金券
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title">限定優惠</h3>
</div>
<div class="panel-body">
割雙眼皮買二送一
</div>
</div>
<div class="panel panel-warning">
<div class="panel-heading">
<h3 class="panel-title">最后換購</h3>
</div>
<div class="panel-body">
買電烤爐送蜂窩煤一斤!
</div>
</div>
</div>
{# 中間面板 #}
<div class="col-md-8"></div>
<div class="col-md-2">
{# 右邊面板#}
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">點擊就送</h3>
</div>
<div class="panel-body">
勞斯萊斯5元代金券
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title">限定優惠</h3>
</div>
<div class="panel-body">
割雙眼皮買二送一
</div>
</div>
<div class="panel panel-warning">
<div class="panel-heading">
<h3 class="panel-title">最后換購</h3>
</div>
<div class="panel-body">
買電烤爐送蜂窩煤一斤!
</div>
</div>
</div>
</div>
</div>
{#修改密碼的彈出框#}
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">修改密碼</h4>
</div>
<div class="modal-body">
{# 設置密碼的更改#}
<form action="{% url 'set_pwd' %}" method="post">
{% csrf_token %}
<div class="form-group">
<label for="id_username">用戶名</label>
<input type="text" id="id_username" name="username" disabled class="form-control" value="{{ request.user.username }}">
</div>
<div class="form-group">
<label for="id_password">初始密碼</label>
<input type="password" id="id_password" name="old_password" class="form-control" >
</div>
<div class="form-group">
<label for="id_new_password">新密碼</label>
<input type="password" id="id_new_password" name="new_password" class="form-control" >
</div>
<div class="form-group">
<label for="id_confirm_password">確認新密碼</label>
<input type="password" id="id_confirm_password" name="confirm_password" class="form-control">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button class="btn btn-primary" id="id_s">提交</button>
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
邏輯流程
登錄功能
1.定義登錄界面
2.添加驗證碼的功能
圖片驗證碼的傳值有三種方式,直接寫地址,文件二進制數據,路徑
驗證碼的實現
通過Image獲取imgobj對象
生成好的img對象交給ImageDraw.Draw 生成畫筆
定義字體樣式,文字大小
產生隨機的驗證碼,利用chr實現,(65,96)大寫,(97,122)小寫,數字直接就是0-9
隨機5個數字,在每個循環里寫入
利用畫筆對象.text((x軸,y軸)坐標,文本,圖片北京,字體樣式)
存儲寫的字
保存到session中的code
然后保存圖片io
最終返回給前端值 return HttpResponse(io_obj.getvalue())
3.給驗證碼綁定點擊事件
每點擊一次url添加一個?用於刷新
4.使用ajax與后端進行交互
將用戶的輸入獲取到手
success回調函數拿到后端返回的數據
根據code的信信息對相關標簽進行渲染
后端得到數據進行驗證
先判斷驗證碼是否正確
然后利用auth模塊判斷密碼是否正確
根據得到對象判斷存在
定義返回字典,返回給前端
return JsonResponse(back_dic)
驗證碼相關推導
def get_code(request):
# 推導步驟一:直接發送后端存在的圖片
# img標簽可以直接返回二進制數據進行顯示
# with open(r'static/img/02b02.jpg.jpg','rb') as f:
# data = f.read()
# return HttpResponse(data)
# 推導步驟二 利用pillow模塊自動生成圖片
'''
from PIL import Image,ImageDraw,ImageFont
Image 生成圖片
ImageDraw 在圖片上寫字
ImageFont 控制字體的樣式'''
# 生成圖片對象
# img_obj = Image.new('RGB',(360,35),'red') 直接放文件的顏色
# img_obj = Image.new('RGB',(360,35),get_random()) # 放rgb模式(255,23,232)
# # 利用文件操作先保存下來
# with open('xxx.png','wb') as f:
# img_obj.save(f,'png')
# # 然后利用文件操作將圖片數據讀取並發送
# with open(r'xxx.png','rb') as f:
# data = f.read()
# return HttpResponse(data)
# 推導步驟三 臨時存儲數據並且能夠隨時取出的地方
'''內存管理模塊
from io import BytesIO,StringIO
BytesIO 保存數據(並且在獲取的時候,是以二進制的方式給你)
StringIO 保存數據(以字符串的形式給你)
'''
# img_obj = Image.new('RGB',(360,35),get_random())
# # # 先生成一個io對象
# # io_obj = BytesIO() # 可以將對象當做是文件的句柄
# # # 存儲數據
# # img_obj.save(io_obj,'png')
# # # getvalue獲取二進制的數據
# # return HttpResponse(io_obj.getvalue())
# 推導步驟四 在圖片上寫字
img_obj = Image.new('RGB',(360,35),get_random())
# 將生成好的圖片對象交給imageDraw
img_draw = ImageDraw.Draw(img_obj) # 生成一個畫筆對象
# 字體樣式,將ttf格式字體,及字體的大小 設置
img_font = ImageFont.truetype('static/fonts/1.ttf',30)
# 產生一個隨機驗證碼 大小寫英文加數字 五位,每一位都可以是大小寫數據
code= ''
for i in range(5):
# chr 根據數字對應字符編碼的英文
upper_str = chr(random.randint(65,90))
lower_str = chr(random.randint(97,122))
random_int = str(random.randint(0,9))
# 隨機選取一個字符
tmp = random.choice([upper_str,random_int,lower_str])
# 向圖片中寫入一個((x軸,y軸)坐標,文本,圖片北京,字體樣式)
img_draw.text((i*60+60,0),tmp,get_random(),img_font)
# 存儲寫的字
code += tmp
print(code)
# 這個驗證碼后面的其他視圖函可能用得上,找到地方保存,並且這個地方的全局視圖函數都可以訪問
request.session['code'] = code
# 圖片保存
io_obj = BytesIO()
img_obj.save(io_obj,'png')
return HttpResponse(io_obj.getvalue())