1. 程式人生 > 其它 >一、BookShop - 基於Django框架的前後端web專案開發實戰

一、BookShop - 基於Django框架的前後端web專案開發實戰

BookShop

目錄:

一、專案介紹

二、專案基本結構和檔案層級

三、各模組主要功能實現邏輯和資料字典

四、其他功能、外掛


一、專案介紹

專案:BookShop圖書商城

技術:Python3.7,Django3.2,mysql5.7

概述:

​ 完整的線上商城流程

功能:

​ 前臺:首頁,註冊,登陸,類別列表,商品詳情,購物車,訂單

​ 後臺:首頁,登陸,使用者管理,類別管理,商品管理

資料字典:

​ 使用者模型、類別模型、圖書商品模型、封面圖模型、購物車模型、訂單模型、訂單詳情模型

​ 根據專案的功能和要求,設計對應的資料庫、表、欄位


二、專案基本結構和檔案層級

1.建立project:bookshop

2.建立APP,包括前臺APP:myhome,後臺APP:myadmin

3.建立 static、templates 資料夾,併為前、後臺分割槽

4.配置 settings.py 檔案,APP、MIDDLEWARE、TEMPLATES、DATA、LANGUAGE、TIME、STATIC

5.建立資料庫:bookshop

6.建立前、後臺首頁並配置檢視和路由


檔案層級

總覽:

後臺APP:

前臺APP:

後臺模板檔案:

前臺模板檔案:


三、各模組主要功能實現邏輯和資料字典

後端功能部分 - Python

1.前後臺通用功能

登陸、登陸檢測、退出登陸

​ 功能說明:前後臺的登陸、退出登陸,以及使用需要登陸才可以進行的操作時,檢測登陸狀態

1.請求方式為GET時,跳轉到登陸頁面,通過form表單將使用者輸入的資料提交到檢視進行驗證

2.請求方式為POST時,接收表單資料,按順序先檢測驗證碼,再根據使用者名稱獲取使用者物件,最後使用 check_password 驗證密碼是否正確,任一環節未通過則提示並跳轉回登陸頁面

3.密碼正確後進行會話控制,建立 request.session['Admin/VipUser'] 記錄使用者ID、使用者名稱、頭像等資料

4.登陸檢測:在需要檢測登陸狀態的檢視中(後臺中介軟體、前臺新增商品到購物車),通過判斷 request.session['Admin/VipUser'] 中元素的值是否為空來獲取登陸狀態

5.退出登陸: 把存放登陸資訊的 request.session['Admin/VipUser'] 內容設為空即可

注:前臺登陸或退出登陸時,如果在商品詳情頁,操作後需跳轉回原頁面。所以在跳轉到登陸頁面時還需通過URL傳遞當前路徑 nextpath = {{ request.path }} ,並在提交登入form表單時把 {{ request.GET.nextpath }} 一起提交到後臺

登陸驗證碼

​ 功能說明:登陸時顯示,需要正確輸入才可以登陸

1.在檢視中匯入 Image、ImageDraw、ImageFont、random、io 模組

2.定義畫面的背景色、長、寬,背景色需要使用 random 隨機生成

3.建立畫面物件和畫筆物件,呼叫畫筆的 point() 函式繪製噪點,噪點位置需要使用 random 隨機生成

4.建立驗證碼備選值(全部字母和數字),使用 random 取4個值進行拼接,作為驗證碼,並存入 session 用於驗證

5.構造字型物件、顏色,繪製4個字元,然後呼叫記憶體檔案處理,把圖片存在記憶體中,每次呼叫函式時返回圖片

分頁

​ 功能說明:在後臺管理頁或商品展示頁可以將展示的內容分頁

1.匯入 Paginator 模組,例項化分頁物件 ,根據當前頁碼數獲取該頁的資料後分配到模板

2.自定義處理分頁的模板標籤,需要顯示的頁碼為:被選中頁碼 p ,被選中頁碼之前的5頁和之後的4頁,共10個頁碼,定義 start=p-5 end=p-4 記錄頁碼範圍(需要控制 startend 不能超出邊界值)

3.在頁碼範圍內迴圈並利用字串拼接 pagehtml += f'<li><a href="?page={i}">{i}</a></li>' ,完成10個頁碼按鈕的顯示,並在首尾拼接:首頁、上一頁、下一頁、尾頁,在首頁時無法點選上一頁,在尾頁時無法點選下一頁。將拼接的最終結果反轉義,然後 return

4.在需要分頁的模板中使用 {% load pagetag %} 匯入自定義的模板標籤,然後呼叫上面拼接頁碼的方法實現輸出

搜尋

​ 功能說明:在後臺管理頁或商品展示頁可以按條件搜尋資料

1.接收搜尋條件,在檢視中獲取搜尋範圍 select 和搜尋框 input 中的值,若非空則繼續判斷搜尋條件 select

2.根據搜尋條件,使用django.db.models 中的 Q 對所有資料進行過濾: data=data.filter(Q(key1__contains=keywords)|Q(key2__contains=keywords)......)

3.將過濾後的資料分配到模板中使用

使用者、類別、商品列表的增刪改

​ 功能說明:在後臺管理頁面中展示各自模組的資料,並提供增、刪、改的功能

1.在載入列表主頁面的檢視中,需要先獲取要展示的資料,預設獲取全部資料,若有搜尋、分頁條件則按條件過濾資料,然後把篩選後的最終資料分配到模板進行呼叫

2.增:在列表頁點選新增,呼叫URL中的檢視跳轉到新增頁;提交新增表單後,使用者模組需要對密碼加密並檢測是否輸入了頭像,類別模組需要判斷類別的層級並設定對應的 path 值,商品列表需要檢測是否有封面,最後呼叫模型儲存資料

3.刪:點選刪除按鈕,前臺將獲取該條資料的id併發送ajax請求到後臺,後臺接收id後獲取對應的物件,使用者模組需要刪除頭像檔案,類別模組需要判斷該類別下是否有子類別,商品模組需要刪除封面圖,最後刪除該物件

4.改:在使用者和商品的編輯方法對應的路由中,新增接收id的引數,點選編輯時獲取該條資料的id並傳到後臺,後臺獲取該物件並將資料和編輯頁面一起返回;提交表單後後臺接收資料,呼叫模型儲存資料後跳轉到列表頁


2.後臺模組

​ 檢視目錄:bookshop\myadmin\views,模板目錄:bookshop\templates\myadmin

(1).使用者管理模組:

​ 檢視:UsersViews.py,模板目錄:users

​ 功能:使用者列表,使用者編輯、新增,刪除,頭像上傳

使用者模型

列名 欄位 型別 備註
ID id auto primary key
使用者名稱 username varchar(20)
密碼 password varchar(80)
手機號 phone char(11) unique
頭像 face_url varchar 儲存頭像的路徑
性別 sex tinyint
年齡 age tinyint
註冊時間 addtime datetime
最後登入時間 savetime datetime
狀態 status int 0:預設,1:禁用

上傳頭像或封面

​ 功能說明:在新增使用者、編輯使用者、新增商品封面時呼叫,完成檔案上傳

1.後臺接收新增、編輯表單後,根據表單內 face 的值判斷是否輸入了圖片,如果沒有輸入則使用預設圖片

2.如果輸入了圖片,需要呼叫上傳圖片檔案的方法,並更新使用者物件中儲存頭像路徑的欄位 face_url

3.上傳圖片的方法:需要先獲取請求中的檔案,根據檔名取出檔案型別,判斷是否為圖片,再用時間戳、隨機數與檔案型別生成新的檔名,然後讀取上傳的檔案內容,寫入新檔案,最後將新檔案路徑 return

(2).類別管理模組:

​ 檢視:ClassifyViews.py,模板目錄:classify

​ 功能:類別列表,類別新增、刪除、編輯

商品類別模型

列名 欄位 型別 備註
ID id auto primary key
類別名稱 name str
父級ID pid int
路徑 path str 頂級分類為0

獲取完成排序且有層次關係的類別

​ 功能說明:在類別管理列表,或新增商品時選擇類別的下拉選單中,把類別排序並按層級顯示

1.使用 extra() 新增新的欄位 paths ,值為 pathid 值拼接的結果,這樣就把同一父級類別下的子類別歸到了一起

2.使用 order_by 對新欄位 paths 排序,即子類別排在父級類別後面

3.類別的層級越低,拼接的次數越多, path 中的 ',' 也就越多,因此可以根據逗號的數量為類別標識出層級,獲取逗號數量: num = i.path.count(',') - 1 ,新增層級標識: i.nbsp = '|---- ' * num

(3).商品管理模組:

​ 檢視:BooksViews.py,模板目錄:books

​ 功能:商品列表,商品新增、刪除、編輯

圖書商品模型

列名 欄位 型別 備註
ID id auto primary key
所屬類別 classifyid ForeignKey 一對多
書名 title varchar
副標題 subhead varchar
作者 author varchar
出版社 publisher varchar
出版時間 pub_date date
定價 price float
庫存數量 num int
國際標準書號ISBN ISBN varchar
內容簡介 context text

圖書封面圖模型

列名 欄位 型別 備註
ID id auto primary key
所屬圖書 bookid ForeignKey 一對多
封面圖url img_url varchar 圖片路徑

商品新增以及一對多模型的應用

​ 功能說明:在商品管理列表新增新的商品

1.請求方式為GET則載入新增商品的模板,為POST則接收新增表單的資料

2.檢測是否有封面圖上傳,沒有則提示並跳轉回商品新增頁面

3.類別模型與圖書模型是一對多的關係,外來鍵為 classifyid ,但form表單中選擇的圖書類別 value 值為類別的 id ,所以需要例項化類別物件,並將接收的表單資料中,欄位 classifyid 值改為類別物件,然後再更新資料到資料庫

4.圖書模型與封面圖模型是一對多的關係,外來鍵 bookid 為圖書物件,img_url 為上傳圖片的方法返回的圖片路徑


3.前臺模組

​ 檢視目錄:bookshop\myhome\views,模板目錄:bookshop\templates\myhome

(1).前臺首頁模組:

​ 檢視:首頁:indexViews.py,註冊:loginViews.py

​ 模板:首頁:index.html、註冊:register.html、分類展示頁:category.html、商品詳情頁:bookdetails.html

​ 功能:商品展示、導航分類展示、商品詳情、註冊、登陸

註冊和註冊驗證

​ 功能說明:使用者註冊時,需要驗證手機號是否已經存在

1.請求方式為GET時,跳轉到註冊頁面,使用者輸入手機號

2.前臺將手機號由ajax請求傳送到後臺檢測是否註冊,後臺根據手機號查詢使用者物件,並將結果返回給前臺

3.請求方式為POST時,後臺接收表單資料,對密碼進行加密,然後呼叫模型儲存使用者資料,並跳轉到登陸頁面

導航分類

​ 功能說明:在導航欄或側面分類列表中,按層級展示所有頂級類別、子類別以及子類別下的商品

1.獲取所有頂級分類物件列表,使用 for 迴圈遍歷列表

2.獲取各個頂級分類下的一級子類,併為每個頂級分類物件新增屬性 sub 記錄這些一級子類物件

3.呼叫時使用迴圈巢狀輸出,外層迴圈遍歷頂級分類列表,內層迴圈遍歷頂級分類的 sub 屬性即子類列表

頂級分類和一級分類跳轉列表

​ 功能說明:在前臺所有頁面都可以通過導航欄點選一個商品類別,跳轉到展示該類別下所有商品的頁面

1.點選導航中的頂級分類或一級分類後,該分類id會通過URL提交到檢視,根據id獲取分類物件,判斷是否為頂級分類

2.如果是頂級分類,需要獲取其所有子類,再遍歷子類列表獲取各子類下的所有圖書物件

3.如果不是頂級分類,需要先獲取其所屬頂級分類,再獲取頂級分類下的其他子類,以及各子類下的圖書物件

(2).購物車模組:

​ 檢視:cartViews.py,模板:cart.html

​ 功能:新增商品、更新商品數量、刪除商品、檢測登陸

購物車模型

列名 欄位 型別 備註
ID id auto primary key
使用者ID uid ForeignKey 一對多
商品ID bid ForeignKey 一對多
數量 num int
是否選中 status int 0:未選,1:選中

新增商品到購物車

​ 功能說明:在商品詳情頁可以將商品加入購物車

1.接收ajax提交的POST表單,然後根據 request.session['vipUser'] 檢測登陸狀態,若沒有登陸則提示

2.根據表單中的商品id獲取商品物件,根據 session 獲取使用者物件

3.判斷加入購物車的商品是否已經存在,根據使用者物件獲取購物車物件,然後用商品id進行篩選,再對篩選結果計數

4.如果已經有該商品的購物車物件,取出這個物件直接更新 num ,如果沒有則建立購物車物件更新資料

編輯數量和刪除商品

​ 功能說明:在購物車頁面編輯商品數量或刪除商品

1.接收ajax提交的GET請求,獲取要刪除或編輯的購物車物件id,以及編輯後的數量

2.獲取購物車物件,執行刪除,或更新 num 並儲存

(3).訂單模組:

​ 檢視:orderViews.py,模板位置:order.html

​ 功能:訂單確認、建立訂單

訂單模型

列名 欄位 型別 備註
ID id auto primary key
使用者ID uid ForeignKey 一對多
收件人 name varchar
手機號 phone varchar
城市 city varchar
詳細地址 address varchar
總價 totalprice float
備註 remark char null=True
狀態 status int 0:未支付,1:已支付
建立時間 ordertime datetime
支付時間 paytime datetime null=True

訂單詳情模型

列名 欄位 型別 備註
ID id auto primary key
訂單ID orderid ForeignKey 一對多
商品ID bid ForeignKey 一對多
商品數量 num int
商品價格 price float

建立訂單

​ 功能說明:接收訂單資料,將資料存入資料庫,並將訂單中的商品從購物車中刪除

1.提交訂單後,接收POST請求中form表單的資料,以及URL攜帶的商品id列表,並根據 session 獲取使用者物件

2.根據商品id列表過濾出訂單中的購物車物件列表,遍歷該列表,計算每個購物車物件的小計價格並累加得到總價

3.將使用者物件、計算得到的總價、表單中使用者輸入的資訊構建成訂單資料,建立訂單物件更新資料

4.再次遍歷購物車物件列表,每一次迴圈更新一個與訂單物件關聯的訂單詳情物件,並刪除該購物車物件


前端功能部分 - js

登錄檔單驗證

​ 功能說明:註冊時,檢測輸入的手機號和密碼格式是否正確,密碼和確認密碼是否相同

1.請求方式為GET時,跳轉到註冊頁面。為手機號、密碼、確認密碼三個 input 框繫結喪失焦點事件,並定義三個變數,記錄三個輸入框的狀態,只有三個變數同時為 true ,登錄檔單才能提交

2.驗證輸入的手機號格式是否正確(第一位為1,後面十位為數字),如果不正確則觸發喪失焦點事件輸入框變為紅色,併為手機號對應的變數賦值 false ;如果手機號格式正確,則傳送 ajax 請求到後臺驗證手機號是否已經註冊,若沒有註冊,則輸入框變為綠色,併為對應的變數賦值 true

3.驗證輸入的密碼和確認密碼格式是否正確(6-18位,字母、數字、下劃線),格式正確再驗證兩個值是否相同,若相同,則輸入框變為綠色,併為對應的變數賦值 true

4.為表單繫結表單提交事件,觸發所有 input 框的喪失焦點事件,並驗證變數,三個變數都為 true 則提交表單

5.後臺接收表單資料,呼叫 make_password 對密碼加密,建立使用者物件並儲存資料,然後提示使用者註冊結果並跳轉到登陸

ajax刪除

​ 功能說明:在後臺管理頁、購物車頁中,不跳轉頁面刪除資料

1.為刪除按鈕繫結單擊事件

2.獲取當前點選項的id,併發送ajax請求到後臺進行刪除

3.判斷響應的結果,如果後臺資料刪除成功,則頁面中刪除該行資料

ajax編輯

​ 功能說明:在後臺類別管理頁,不跳轉頁面,雙擊類別名進行編輯

1.為需要編輯的項繫結雙擊事件並定義執行編輯的函式

2.獲取雙擊項中的內容 name 和該條資料的id,建立 input 框顯示 name

3.為 input 框繫結喪失焦點事件,觸發後,獲取編輯後的 name

4.判斷 name 值是否發生改變,沒有改變則還原,改變則傳送ajax請求到後臺更新 name

5.判斷響應的結果,顯示更新後的 name

圖片預覽

​ 功能說明:在上傳頭像或封面圖時,預覽圖片

1.為上傳控制元件繫結 change 事件,值改變時觸發

2.根據欄位的長度判斷是否輸入了圖片,如果沒有輸入則繼續使用預設頭像

3.輸入了頭像,獲取圖片物件,用 FileReader() 例項化檔案讀取物件,讀取上傳的檔案資料為 readAsDataURL(file)

4.為檔案讀取物件繫結 onload 事件,物件載入時觸發,顯示讀取到的檔案 FileReader.result

加入購物車時傳遞商品資料

​ 功能說明:在商品詳情頁點選加入購物車時,要先登入,然後傳遞商品資料到後臺,同時傳遞路徑用於登陸後跳轉

1.給加入購物車按鈕繫結單擊事件

2.點選時,根據 request.session.vipUser.id 判斷登陸狀態,沒有登陸則提示,獲取路徑值並跳轉到登陸頁面

3.若已登入,獲取商品id和數量框中的值,傳送ajax請求到後臺,把資料新增到購物車

購物車內根據商品數量顯示價格

​ 功能說明:在購物車內,商品的小計價格隨數量改變而改變

1.為購物車內商品數量框繫結喪失焦點事件

2.觸發時,獲取數量框內的值 n 、該商品的id、商品單價

3.傳送ajax請求將編輯後的數量 n 和商品id傳到後臺更新商品資料

4.根據響應判斷是否更新成功,成功則計算當前價格,並修改小計標籤的文字內容為計算後的結果

購物車內全選商品

​ 功能說明:在購物車內點選全選時選中所有商品

1.為全選框繫結單擊事件

2.當點選全選時,使用 prop('checked',this.checked) 為所有選項框設定與全選框相同的狀態

3.呼叫計算商品總價的方法顯示總價

計算商品總價

​ 功能說明:根據選中的商品及數量,計算商品總價

1.定義變數記錄選中的商品和價格,起始總價值為0,選中商品列表為空列表

2.迴圈所有 checkboxcheckedtrue 表示選中,獲取該商品的小計價格累加進總價,獲取商品id放入列表

3.修改總價標籤的文字內容為計算後的結果,返回被選中商品id列表


四、其他功能、外掛

CSRF驗證

取消保護

​ 如果某些檢視不需要保護,可以使用裝飾器 csrf_exempt,模板中也不需要寫標籤

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def func(request):
    data = request.POST['name']
    return render(request,'myadmin/index.html',{'data':data})

Ajax CSRF 驗證

​ POST 請求需要正確驗證才能返回結果

​ 使用 Ajax 呼叫的時候,需要在使用 jQuery 的 ajax 或 post 之前加入以下 js 程式碼(要寫在模板檔案中)

$.ajaxSetup({
           data:{csrfmiddlewaretoken:'{{ csrf_token }}'},
        });

會話控制

​ 在請求中,http是無狀態請求協議。http無法記錄使用者的請求狀態,如第幾次請求、在什麼情況下請求、請求前後訪問過哪裡,但在web專案中有時是需要記錄一些狀態的,如是否登入、檢視過哪些頁面、獲取使用者偏好等

可以記錄資訊的技術:

  • cookie

    • cookie是在瀏覽器中記錄資料,瀏覽器在訪問同一伺服器時會主動攜帶該資料
    • 如果cookie丟失,或者換了瀏覽器訪問,那麼cookie就不再存在
    • 敏感資料存在瀏覽器中有安全隱患
  • session

    • session是在伺服器中記錄資料(檔案、資料庫),每個資料生成一個唯一的ID,叫做sessionID
    • sessionID作為cookie儲存在使用者的瀏覽器中,session依賴cookie
    • 可記錄的資料量較大,資料相對安全

使用 session

​ 使用 django-admin startproject 建立的專案預設啟用,啟用會話後,每個 HttpResponse 物件將具有一個 session 屬性,session 是一個類字典物件

# 新增資料
request.session['key'] = value

# 獲取資料
request.session.get('key',default=Node)

# 刪除資料
del request.session['key']

# 清除所有會話,不會刪除資料
rquest.session.clear()

# 刪除當前的會話資料
request.session.flush()

中介軟體

​ 中介軟體是介於請求和響應之間的一個元件,用於全域性更改 Django 的輸入(請求)或輸出(響應),可以對請求或響應進行攔截、篩選、過濾等處理,如CSRF中介軟體

​ get_response 就是需要響應的內容,可能是檢視,也可能是下一個中間,但當前的中介軟體不需要知道它到底是什麼,只要知道它代表接下來發生的事即可

啟用中介軟體

​ 自定義的中介軟體需要把自定義類配置在 settings 下的 MIDDLEWARE 中才會生效,格式為:' 包名.模組名.類名 '

驗證碼

​ 註冊、登陸頁面,為了防止暴力請求可以加入驗證碼功能,如果驗證碼錯誤,則不需要繼續處理,以減輕伺服器壓力

​ 使用驗證碼也是一種有效防止CSRF的方法

驗證碼檢視

  • 匯入 PIL 中的 image、imageDraw、imageFont 模組,分別為畫布物件、畫筆物件、字型物件
  • 定義生成驗證碼的函式 verifycode
  • 配置驗證碼函式的url,並在模板中呼叫