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
記錄頁碼范圍(需要控制 start
和 end
不能超出邊界值)
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
,值為 path
和 id
值拼接的結果,這樣就把同一父級類別下的子類別歸到了一起
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.循環所有 checkbox
, checked
為 true
表示選中,獲取該商品的小計價格累加進總價,獲取商品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,並在模板中調用