django實戰專案課程平臺搭建

Python實戰講師團發表於2019-12-04

一需求:

具備的功能:

1. 業務邏輯分析

1.1 要實現的功能
1.1.1 使用者可用功能
1.1.2 後臺管理功能
1.2 要建立的app
1.2.1 course(課程)
1.2.2 user(使用者)

2. Model層開發

2.1 建立Django專案
2.2 分析表結構和建立
2.2.1 使用者表:User
2.2.2 課程表:Course
2.2.3 課程種類表:Category
2.2.4 表結構的建立
2.3 表結構的遷移
2.4 建立儲存網頁資源的資料夾

3. Admin後臺管理

3.1 建立超級管理員賬戶
3.2 登入後臺管理系統
3.3 註冊建立的模型類
3.4 配置後臺管理頁面的顯示內容
3.5 配置app在後臺顯示的名稱
3.6 配置模型類表的顯示中⽂名稱
3.7 定義某些欄位可以為空

4. 路由與檢視

4.1 在建立的app中建立urls.py檔案
4.2 在總路由檔案urls.py中載件兩個app的urls.py檔案
4.3 在course的urls.py檔案中配置路由對映
4.4 在course的views.py檢視檔案中建立對應的檢視函式
4.5 在user的urls.py檔案中配置路由對映
4.6 在user的views.py檢視檔案中建立對應的檢視函式

5. 商品首頁後端資料渲染

5.1 建立靜態網頁資源
5.1.1 在static資料夾下
5.1.2 在templates資料夾中
5.1.3 在settings.py中配置靜態資
5.2 抽離html模板的各個模組
5.3 根據各個html頁面需求,引⽤抽離的模組並做相應的修改

6. 使用者個人管理頁面

6.1 配置使用者註冊頁面
6.2 建立中介軟體
6.3 配置使用者登入介面
6.4 配置使用者登出介面
6.5 配置使用者主頁base.html

7. 購物車功能完善

7.1 商品詳細頁
7.2 新增購物車與展示
7.3 購買課程與已購買課程

8. 視訊傳輸許可權與協議
9. 網站安全性優化

後端管理功能
1.1課程平臺首頁展示

1.2登入註冊功能
在這裡插入圖片描述
在這裡插入圖片描述
1.3課程詳情頁
在這裡插入圖片描述
1.4加入購物車功能,立即購買功能
在這裡插入圖片描述在這裡插入圖片描述
1.5個人詳情頁
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述1.6觀看功能
在這裡插入圖片描述
2.1後端管理功能
在這裡插入圖片描述
2.2使用者表
在這裡插入圖片描述
2.3課程表
在這裡插入圖片描述
在這裡插入圖片描述

二.程式碼實現

1. 業務邏輯分析
1.1 要實現的功能

1.1.1 使用者可用功能
使用者註冊
使用者登入、修改客戶資料
使用者登出
使用者購買課程以及檢視已購買課程
使用者把課程加入購物車以及檢視購物車
1.1.2 後臺管理功能
使用者表——管理使用者(增刪改使用者資訊及使用者餘額等)
上線課程——管理課程(增加新課程、上傳課程相關檔案和資訊)
課程種類——管理課程型別(新增課程型別)

1.2 要建立的app

1.2.1 course(課程)
商城主頁
頁面展示所有課程的名稱和價格,課程按課程型別排列
課程詳細頁
展示該課程的詳細資訊:名稱、建立日期、價格,根據使用者情況提供加入購物車、購買課程、直接觀看
視訊的介面
視訊播放頁
調視訊流介面,實現安全播放視訊功能
視訊流介面
用安全的方式,把視訊以視訊流的形式輸出到瀏覽器客戶端
1.2.2 user(使用者)
使用者主頁
通過get方法,展示使用者資訊:賬號、戶名、餘額、性別等
通過post方法,修改介面
已購買課程頁面
展示使用者已購買的所有課程,可以直接進入觀看
購物車頁面
展示使用者購物⻋內的所有課程
登入、註冊、登出介面
根據不同場景提供不同的介面,並確保在未登陸情況下不可以訪問需要登陸後或購買課程後才可以訪問
的頁面,互相之間不能造成影響
課程購買介面
加入購物車介面

2. Model層開發
2.1 建立Django專案

直接在PyCharm中建立Django專案,同時建立一個user的app
開啟settings.py,配置資料庫

DATABASES = {
	'default': {
	'ENGINE': 'django.db.backends.mysql',
	'HOST': 'localhost',
	'PORT': '3306',
	'NAME': 'csdn_course', # 資料庫名稱儘量跟專案名稱一致
	'USER': 'root',
	'PASSWORD': '123456'
 }
}

在MySQL中建立這個相應的資料庫
修改settings.py中的語言和時區設定

LANGUAGE_CODE = 'zh-hans' # 中國
TIME_ZONE = 'Asia/Shanghai' # 東八區時區
2.2 分析表結構和建立

2.2.1 使用者表:User
在這裡插入圖片描述
2.2.2 課程表:Course
在這裡插入圖片描述
2.2.3 課程種類表:Category
在這裡插入圖片描述
2.2.4 表結構的建立

from django.db import models
#使用者表, 可以留空的欄位傳blank=True引數, 表單提交時可為空
class User(models.Model):
	class Meta(): 
#建立Meta類, 配置verbose_name, 讓頁面顯示欄位的中文名稱
		verbose_name = verbose_name_plural = '使用者表'
account = models.CharField(max_length=16, unique=True, verbose_name='賬 號')
#賬號唯一, 傳入verbose_name引數使其顯示中文名稱
password = models.CharField(max_length=16, verbose_name='密碼')
username = models.CharField(max_length=16, verbose_name='使用者名稱',blank=True)
money = models.DecimalField(max_digits=12, decimal_places=2,default=0, verbose_name='餘額') 
#總位數12,小數位2
#0:男,1:女,預設0,傳入choices=((0, '男'), (1, '女'))引數, 在後臺提供下拉選項,choices裡面的元素為元組
gender = models.PositiveSmallIntegerField(default=0, verbose_name='性 別', choices=((0, '男'), (1, '女')))
tel = models.CharField(max_length=11, default='',verbose_name='手機號', blank=True)

def __str__(self):
	return self.account
# 定義__str__方法, 返回欄位account, 在頁面中直接顯示該欄位的名稱

課程表

from django.db import models
import datetime
from user.models import User
import os

# 定義儲存課程視訊路徑的方法
def save_file(instance, filename):
    # 視訊儲存在根目錄下的video資料夾內
    return os.path.join('video', filename)

# 定義儲存課程圖片路徑的方法
def save_img(instance, filename):
    # 圖片儲存在根目錄下static資料夾下的img資料夾內
    return os.path.join('static', 'img', filename)
    
# 課程種類表, 傳入verbose_name引數讓頁面顯示中文名稱
class Category(models.Model):
    class Meta():  # 建立一個Meta類, 配置verbose_name, 讓頁面顯示欄位的中文名稱
        verbose_name = verbose_name_plural = '課程種類表'

    name = models.CharField(max_length=50, unique=True, verbose_name='課程種類')

    def __str__(self):  # 定義__str__方法, 返回欄位name, 在頁面中直接顯示欄位的名稱
        return self.name

# 課程表, 傳入verbose_name引數讓頁面顯示中文名稱, blank=True允許表單傳入資料為空
class Course(models.Model):
    # 建立一個儲存收費與否的元組, 傳入status作為引數, 提供顯示中文名稱的下拉選單
    STATUS_CHOICES = (
        (0, '收費'),
        (1, '免費')
    )
    class Meta():  # 建立一個Meta類, 配置verbose_name, 讓頁面顯示欄位的中文名稱
        verbose_name = verbose_name_plural = '課程表'

    courseName = models.CharField(max_length=40, verbose_name='課程名稱')
    fileName = models.FileField(upload_to=save_file, verbose_name='檔名稱')   # upload_to引數呼叫儲存視訊路徑方法拿到視訊儲存路徑
    imgname = models.ImageField(upload_to=save_img, verbose_name='課程圖片')    # upload_to引數呼叫儲存圖片路徑方法拿到圖片儲存路徑
    # 設定課程種類的外來鍵連線, 一對多的關係, 設定反向查詢的related_name, 便於反向查詢
    pCategory = models.ForeignKey(to=Category, related_name='courses_set', on_delete=models.CASCADE,
                                  verbose_name='課程類別')
    price = models.DecimalField(max_digits=7, decimal_places=2, default=0, verbose_name='售價', blank=True)
    summary = models.CharField(max_length=1000, default='', verbose_name='課程介紹', blank=True)
    status = models.PositiveSmallIntegerField(default=0, verbose_name='狀態', blank=True,
                                              choices=STATUS_CHOICES)  # 0:收費 1:免費,預設0
    createDatetime = models.DateTimeField(default=datetime.datetime.now(), verbose_name='建立時間', blank=True)
    # 定義已購買課程使用者跟課程的多對多關係, 設定反向查詢的related_name
    userBuyer = models.ManyToManyField(to=User, related_name='userBuyer_set', verbose_name='購買使用者', blank=True)
    # 定義加入購物車的使用者跟課程的多對多關係, 設定反向查詢的related_name
    userShoppingcart = models.ManyToManyField(to=User, related_name='userShoppingcart_set', verbose_name='加入購物車的使用者',
                                              blank=True)

課程種類表

# 課程種類表, 傳入verbose_name引數讓頁面顯示中文名稱
class Category(models.Model):
	#建立一個Meta類, 配置verbose_name, 讓頁面顯示欄位的中文名稱
	class Meta(): 
		verbose_name = verbose_name_plural = '課程種類表'
	name = models.CharField(max_length=50, unique=True, verbose_name='課程種類')
	def __str__(self): 
		# 定義__str__方法 返回欄位name, 在頁面中直接顯示欄位的名稱
		return self.name

2.3 表結構的遷移
在終端中生成遷移檔案

python3 manage.py makemigrations

在終端中行遷移檔案

python3 manage.py migrate

2.4 建立儲存網頁資源的資料夾
在根錄中建立stati資料夾,在static資料夾中建立img資料夾用來儲存上傳的圖片檔案
在根目錄中建立video資料夾,用來儲存上傳的視訊檔案
3. Admin後臺管理
3.1 建立超級管理員賬戶
django會自動生成admin後臺管理頁面,可以通過管理員賬戶直接登入進入後臺管理
在終端中執行以下程式碼建立超級管理員賬戶

python3 manage.py createsuperuser

輸入使用者名稱、郵箱、密碼即可建立
3.2 登入後臺管理系統
通過主頁/admin進入後臺管理登入頁面,輸入賬號和密碼登入,即可進入後臺管理介面
3.3 註冊建立的模型類
在每個app資料夾下的admin.py檔案中,註冊建立的模型類
course app

from django.contrib import admin
from .models import Course, Category
# 匯入所有的模型類
# 註冊這個app內建立的模型類
@admin.register(Category) # 傳⼊模型類名作為引數
class CategoryAdmin(admin.ModelAdmin): 
 #建立這個類名的Admin類, 繼承admin.ModelAdmin類
	list_display = ['name'] 
	# 設定該模型類中要顯示的欄位
	search_fields = ['name'] 
	#設定可以通過哪個欄位進行查詢
@admin.register(Course)
class CourseAdmin(admin.ModelAdmin):
	filter_horizontal = ['userBuyer', 'userShoppingcart'] 
	#設定側邊欄顯示這兩個欄位, 介面對使用者比較友好
list_display = ['id', 'courseName', 'pCategory', 'price', 'summary','status', 'createDatetime'] 
#設定該模型類中要顯示的欄位
list_filter = ['status', 'createDatetime'] 
# 設定過濾器, 選擇可以通過哪個欄位來過濾顯示
search_fields = ['courseName', 'price'] 
#設定可以通過哪個欄位進⾏查詢搜尋

user app

from django.contrib import admin
from .models import User 
# 匯入所有的模型類
# 註冊這個app內建立的模型類
@admin.register(User) # 傳入模型類名作為引數   
class UserAdmin(admin.ModelAdmin): 
# 建立這個類名的Admin類, 繼承admin.ModelAdmin類
	list_display = ['id', 'account', 'username', 'money', 'gender', 'tel'] 
# 設定要顯示的欄位名稱
	list_filter = ['gender'] 
# 設定可以通過哪個欄位進⾏篩選
	search_fields = ['account', 'username'] 
# 設定可以通過哪個欄位進⾏搜尋查詢

3.4 配置後臺管理使用者的顯示內容
在任意一個admin.py中,加入以下程式碼

admin.site.site_header = 'CSDN微課後臺管理' #頁面中顯示標題
admin.site.index_title = '後臺系統' # 標籤頁中顯示字首
admin.site.site_title = '管理' # 標籤中顯示字尾

3.5 配置app在後臺顯示的名稱
在每個app資料夾下的apps.py檔案中,建立app在後臺的顯示名稱

  • course
from django.apps import AppConfig
class CourseConfig(AppConfig):
	name = course
	#配置verbose_name  讓頁面顯示中文
	verbose_name = '課程管理'
  • user
from django.apps import AppConfig
class UserConfig(AppConfig):
	name = 'user'
	# 配置verbose_name, 讓頁面可以顯示欄位的中文名稱
	verbose_name = ''使用者管理'

3.6 配置模型類表的顯示中⽂名稱
在每個app的models.py檔案中,在每個類之中加入以下程式碼

  • course
# 課程種類表
class Category(models.Model):
	class Meta():
		verbose_name = verbose_name_plural = '課程種類表'

  • category
# 課程表:
class Course(models.Model):
	class Meta():
		verbose_name = verbose_name_plural = '課程表'
  • user
#使用者表
class User(models.Model):
	class Meta():
		verbose_name = verbose_name_plural = '使用者表'

3.7 定義某些欄位可以為空
在模型類中該欄位的傳入引數中新增blank=True即可
4. 路由與檢視
4.1 在建立的app中建立urls.py檔案
4.2 在總路由檔案urls.py中載入兩個app的urls.py檔案

from django.contrib import admin
from django.urls import path, include
urlpatterns = [
	path('admin/', admin.site.urls),
	path('', include('course.urls')), # 載人course的urls檔案
	path('user/', include('user.urls')), # 載入app的urls檔案
]

4.3 在course的urls.py⽂件中配置路由對映

from django.urls import path, re_path
from . import views
urlpatterns = [
	path('', views.index_handler, name='course_index'), 
	# 定義商城主頁的路由
	re_path('course/(.+)', views.course_handler, name='course_course'),
	# 通過正則匹配課程的詳細頁面, 傳入course_id作為引數
	re_path('video/(.+)', views.video_handler, name='course_video'), 
	# 匹配視訊播放頁面, 傳入course_id作為引數獲取視訊
	re_path('videoStream/(.+)', views.videoStream_handler,name='course_videoStream'), 
	# 匹配視訊流檔案, 根據course_id獲取
]

4.4 在course的views.py檢視檔案中建立對應的檢視函式

# 對應檢視函式的設定見後面具體章節的配置

4.5 在user的urls.py檔案中配置路由對映

from django.urls import path, re_path
from . import views
urlpatterns = [
	path('', views.index_handler, name='user_index'), 
	# 定義使用者主頁的路由
	path('course', views.course_handler, name='user_course'), 
	# 定義使用者已購買課程頁面的路由
	path('shoppingCart', views.shoppingCart_handler,name='user_shoppingCart'), 
	# 定義使用者購物車頁面的路由
	path('login', views.login_handler, name='user_login'), 
	# 定義登入接的路由
	path('register', views.register_handler, name='user_register'),
	# 定義註冊接入的路由
	path('logout', views.logout_handler, name='user_logout'),
	 # 定義登出接入的路由
	re_path('purchase/(.+)', views.purchase_handler, name='user_purchase'), 
	# 定義購買課程接入的路由		                 						                                       
	re_path('addShoppingCart/(.+)', views.addShoppingCart_handler,name='user_addShoppingCart') 
	# 定義加入購物車接入的路由
	]

4.6 在user的views.py檢視檔案中建立對應的檢視函式

# 對應檢視函式的設定見後面具體章節的配置
  1. 商品頁面後端資料渲染
    5.1 建立靜態檔案資源
    5.1.1 在static資料夾下
    建立CSS資料夾,放在css檔案
    建立js資料夾,放在js檔案
    5.1.2 在templates檔案件夾中
    建立base.html檔案(直接提供的模板),作為所有檢視頁面的引用模板
    建立各個檢視函式對應的渲染頁面

5.1.3 在settings.py中配置靜態資源

#在settings.py檔案的最後新增以下程式碼
STATIC_URL = '/static/'
STATICFILES_DIRS = [
	os.path.join(BASE_DIR, 'static')
 ]

5.2 抽離html模板的各個模組
5.3 根據各個html頁面需求,引入抽離的模組並做相應的修改
6. 使用者個人頁面管理
6.1 配置使用者註冊頁面

檢視函式程式碼

  • 使用者註冊檢視函式
# 使用者註冊檢視函式
def register_handler(request):
    if request.method != 'POST':  # 判斷如果請求方式不是post, 返回403錯誤
        return HttpResponse(status=403)
    context = request.context  # 通過request先處理context內容
    account = request.POST.get('account')  # 獲取賬號和密碼
    password = request.POST.get('password')
    try:
        user_exists = User.objects.filter(account=account).exists()  # 判斷賬號是否已經存在
        if not user_exists:
            user = User(account=account, password=password)  # 如果不存在, 則儲存使用者註冊的賬號和密碼
            user.save()
            request.session['session_user'] = {'id': user.id, 'account': user.account}  # 並儲存賬戶登入的session
        else:
            context['register_message'] = '賬號已存在'  # 如果已存在賬號, 返回一個提示資訊
    except:
        context['register_message'] = '伺服器異常'   # 如果執行到except程式碼塊, 提示伺服器異常
    finally:
        return course_views.index_handler(request)  # 最終返回所有課程的商城主頁

  • base.html頁面程式碼
<!-- 註冊 -->
<div id="register"
	{% if not register_message %}
	{# 如果出現提示異常的資訊, 註冊的標籤取消隱藏 #}
	hidden="hidden">
 	{% endif %}
	<h2 class="form_p">註冊</h2> 
	<p id="register_message">
 		{{ register_message }}
		<!--資訊有誤-->
	</p> 
<form action="{% url 'user_register' %}" method="post"  id="register_form">
	{% csrf_token %}
	<input id="register_account"  type="text"  name="account"placeholder="賬號(數字、英⽂、下換線,8-16位)"><br/>
	<input id="register_password" type="password" name="password"placeholder="密碼(數字、英⽂、下換線,6-16位)"><br/>
	<!--<input type="password" name="repassword" placeholder="確認密碼"><br/>-->
	<input id="register_submit" type="submit" value="註冊">
</form>
</div>

6.2 建立中介軟體在根目錄新建個middleware.py檔案,插入以下程式碼

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import reverse
from course import views as course_views

class MyMiddleware(MiddlewareMixin):
    def __init__(self,get_response=None):
        super().__init__(get_response)
        # 初始化中介軟體
        print('init_mymiddleware')

    def process_request(self,request):
        # request.context={}
        # session_user=None
        # if 'session_user' in request.session.keys():
        #     request.context['session_user']=request.session['session_user']
        # if not session_user:
        #     if request.path.startswith('/video') or request.path.startswith('/user'):
        #         if request.path not in [reverse('user_login'),reverse('user_rigister')]:
        #             request.context['login_message']='請先登入'
        #         return course_views.index_handdler(request)
        request.context = dict(
            session_user = request.session['session_user'] if'session_user' in request.session.keys() else None
        )
        if (not request.context['session_user'])\
                and (request.path.startswith('/video') or request.path.startswith('/user')) \
                and request.path not in [reverse('user_login'),reverse('user_register')]:
                request.context['login_message'] = '請先登入'
                return course_views.index_handler(request)


    def process_response(self,request,response):
        # 必須return response
        print('process_response')
        return response

6.3 配置使用者登入檔案

  • 檢視函式
# 使用者登入檢視函式
def login_handler(request):
	if request.method != 'POST':
		# 如果請求方式不是post, 返回403禁止訪問
		return HttpResponse(status=403)
	context = request.context
	account = request.POST.get('account')
	password = request.POST.get('password')
	user_s = User.objects.filter(account=account, password=password) 
	#查詢使用者物件中是否存在匹配的賬戶和密碼
	if user_s:
		user = user_s[0] # 如果存在, 則獲取第⼀個匹配的⽤戶
		request.session['session_user'] = {'id': user.id, 'account':user.account} 
		# 建立session_user
		return redirect(reverse('course_index')) 
		# 登入成功後重定向到商城主頁

		# context['session_user'] = {'id': user.id, 'account':user.account}
	else:
		context['login_message'] = '賬號或密碼錯誤!' 
		# 如果不存在對應的賬戶和密碼, 提示錯誤資訊
		return course_views.index_handler(request) 
		# 返回頁面檢視

base.html

<div id="login"
     {% if not login_message %}
     hidden="hidden"
     {% endif %}>
    <h2 class="form_p">登入</h2>
    <p id="login_message">
        <!--資訊有誤-->
        {{login_message}}
    </p>
    <form action="{% url 'user_login' %}" method="post" id="login_form">
        {% csrf_token %}
        <input id="login_account" type="text" name="account" placeholder="賬號"><br>
        <input id="login_password" type="password" name="password" placeholder="密碼"><br>
        <input id="login_submit" type="submit" value="登入">
    </form>
</div>

6.4 配置使用者登出頁面

  • 檢視函式
# 使用者登出登入檢視函式
def logout_handler(request):
	# 如果使用者點選登出, 把使用者的session設定為None
	request.session['session_user'] = None
	# 頁面重定向到商城主頁
	return redirect(reverse('course_index'))

6.5 配置使用者主頁user.html

  • 檢視函式
def index_handler(request):
	context = request.context # 獲取中介軟體處理的context
	session_user = request.session['session_user'] # 獲取登入的使用者
	user = User.objects.get(id=session_user.get('id')) # 獲取使用者資訊
	context['user'] = user
	if request.method == 'GET': 
	# 如果是get請求, 直接返回使用者主頁詳情
		return render(request, 'user.html', context)
	else: 
	# 如果是post請求, 要獲取賬戶名/性別/電話, 並儲存使用者資訊
		user.username = request.POST.get('username')
		user.gender = request.POST.get('gender')
		user.tel = request.POST.get('tel')
		user.save()
		return redirect(reverse('user_index')) # 重定向到使用者主頁

user.html

{% extends 'base.html' %}
{% block title %}
{% endblock %}
{% block article %}
    <article>
        <section class="main_section">
            <hr><section id="user_manage">
                <nav>
                    <p><a href="{% url 'user_index' %}">個人資料</a></p>
                    <p><a href="{% url 'user_course' %}">我的課程</a></p>
                    <p><a href="{% url 'user_shoppingCart' %}">購物車</a></p>
                </nav>
                <section class="user_info">
                    <div class="user_info_wrapper">
                        <ul>
                            <li>
                                <label>賬&#12288;號:</label><span>{{ user.account }}</span>
                            </li>
                            <li>
                                <label>餘&#12288;額:</label><span>{{ user.money }}</span>
                            </li>
                        </ul>
                        <form action="{% url 'user_index' %}" method="post">
                            {% csrf_token %}
                            <ul>
                                <li>
                                    <label>使用者名稱:</label>
                                    <input type="text" name="username" value="{{ user.username }}">
                                </li>
                                <li>
                                    <label>性&#12288;別:</label>
                                    {% if not user.gender %}
                                        <input type="radio" name="gender" value="0" checked="checked">男
                                        <input type="radio" name="gender" value="1">女
                                    {% else %}
                                        <input type="radio" name="gender" value="0">男
                                        <input type="radio" name="gender" value="1" checked="checked">女
                                    {% endif %}
                                </li>
                                <li>
                                    <label>手機號:</label>
                                    <input type="text" name="tel" value="{{ user.tel }}">
                                </li>
                                <li>
                                    <input type="submit" value="修改">
                                    <input type="reset" value="重置">

購物車功能完善
7.1 商品詳細頁

  • 檢視函式
# 定義課程商品詳細頁檢視函式
def course_handler(request, course_id):
    context = request.context                       # 通過request先處理context再傳入函式中
    try:                                            # 如果課程存在, 則返回課程頁面
        course = Course.objects.get(id=course_id)
        session_user = request.sessi	on.get('session_user', None)
        if session_user:
            # 使用者登入以後, 通過使用者id查詢該使用者是否已經購買該課程, 判斷是否允許其觀看對應課程的視訊
            context['view_permission'] = User.objects.filter(id=session_user.get('id'), userBuyer_set__id=course.id).exists()
        context['course'] = course
        return render(request, 'course.html', context)
    except:                                         # 如果課程不存在, 返回404頁面
        return HttpResponse(status=404)

course.html

{% extends 'base.html' %}
{% block title %}
    {{ course.pCategory.name }}
{% endblock %}
{% block article %}
    <article>
        <section class="main_section">
            <section class="course_detail">
                <img src="/{{ course.imgname }}" alt="">
                <div class="course_detail_div">
                    <h1>{{ course.courseName }} </h1>
                    <p>課程種類:{{ course.pCategory.name }}</p>
                    <p>釋出時間:{{ course.createDatetime| date:'Y-m-d' }}</p>
                    <p>價格:<span class="price">{{ course.price }}</span></p>
                    <p>
                        {% if not view_permission %}
                        <a href="{% url 'user_addShoppingCart' course.id %}">加入購物車</a>
                        <a href="{% url 'user_purchase' course.id %}">立即購買</a>
                        {% else %}
                        <a href="{% url 'course_video' course.id %}">立即觀看</a>
                        {% endif %}
                    </p>
                </div>
            </section>
            <section class="course_summary">
                <h2>[課程介紹]</h2>
                <p class="summary_p">
                    {{ course.summary }}
                </p>
            </section>
        </section>
    </article>
{% endblock %} 

7.2 新增購物車與展示

  • 檢視函式
def addShoppingCart_handler(request, course_id):
    context = request.context       # 獲取request傳遞過來的context
    try:
        course = Course.objects.get(id=course_id)       # 通過course_id獲取對應的課程
        session_user = request.session['session_user']  # 獲取登入的使用者
        user = User.objects.get(id=session_user.get('id'))  # 根據使用者的id獲取使用者物件
        user.userShoppingcart_set.add(course)               # 把課程新增進使用者的購物車中
        user.save()                                         # 儲存使用者物件
        context['message'] = '新增購物車成功'
    except:
        context['message'] = '新增購物車失敗'
    return render(request, 'user_message.html', context)    # 返回使用者資訊結果檢視
# 使用者購物車檢視函式
def shoppingCart_handler(request):
    context = request.context       # 獲取request傳入的context
    session_user = request.session['session_user']      # 獲取登入的使用者
    course_s = User.objects.get(id=session_user.get('id')).userShoppingcart_set.all()   # 獲取使用者已經加入購物車的所有課程
    context['course_s'] = course_s      # 封裝到context裡面
    return render(request, 'user_shoppingcart.html', context)   # 傳入到返回的購物車頁面


user_message.html

{% extends 'base.html' %}
{% block title %}
{% endblock %}
{% block article %}
    <article>
        <section class="main_section">
            <hr>
            <section id="user_manage">
                <nav>
                    {#  根據路由對映, 找到對應的檢視  #}
                    <p><a href="{% url 'user_index' %}">個人資料</a></p>
                    <p><a href="{% url 'user_course' %}">我的課程</a></p>
                    <p><a href="{% url 'user_shoppingCart' %}">購物車</a></p>
                </nav>
                <section class="user_info">
                    <div class="user_info_wrapper">
                        <h1>
                            {{ message }}
                        </h1>
                    </div>
                </section>
            </section>
        </section>
    </article>
{% endblock %}

user_shoppingcart.html

{% extends 'base.html' %}
{% block title %}
{% endblock %}
{% block article %}
    <article>
        <section class="main_section">
            <hr>
            <section id="user_manage">
                <nav>
                    <p><a href="{% url 'user_index' %}">個人資料</a></p>
                    <p><a href="{% url 'user_course' %}">我的課程</a></p>
                    <p><a href="{% url 'user_shoppingCart' %}">購物車</a></p>
                </nav>
                <section class="user_info">
                    <div class="user_info_wrapper">
                        <table id="user_shopping_cart" cellpadding="0" cellspacing="0">
                            <tr id="user_shopping_cart_tr_first">
                                <th width="250px">課程名稱</th>
                                <th width="100px">價格</th>
                                <th width="200px">上線時間</th>
                                <th width="100px">操作</th>
                            </tr>
                            {% for course in course_s %}
                            <tr class="user_info_shopping_tr">
                                <td>{{course.courseName}}</td>
                                <td style="color:red;">{{ course.price }}</td>
                                <td>{{ course.createDatetime|date:'Y-m-d' }}</td>
                                <td><a href="{% url 'user_purchase' course.id %}">立即購買</a></td>
                            </tr>
                            {% endfor %}
                        </table>
                    </div>
                </section>
            </section>
        </section>
    </article>
{% endblock %}

7.3 購買課程與已購買課程

  • 使用者購買課程的檢視函式
# 使用者購買課程檢視函式
def purchase_handler(request, course_id):
    context = request.context
    try:
        course = Course.objects.get(id=course_id)       # 通過course_id獲取到課程物件
        session_user = request.session['session_user']  # 獲取登入客戶
        user = User.objects.get(id=session_user.get('id'))
        if user.money >= course.price:                  # 如果使用者餘額大於課程價格, 直接購買, 減少相應的價格
            user.userBuyer_set.add(course)
            user.money -= course.price
            user.save()                                 # 儲存使用者購買資訊, 並提示成功
            context['message'] = '購買成功!'
        else:
            context['message'] = '餘額不足!'             # 否則提示餘額不足
    except:
        context['message'] = '購買失敗!'
    finally:
        return render(request, 'user_message.html', context)    # 返回使用者資訊頁面, 傳入提示資訊
  • 使用者已購買課程檢視函式
# 使用者已購買課程檢視函式
def course_handler(request):
    context = request.context
    session_user = request.session['session_user']      # 獲取登入使用者
    course_s = User.objects.get(id=session_user.get('id')).userBuyer_set.all()  # 通過user_id獲取已購買的課程
    context['course_s'] = course_s
    return render(request, 'user_course.html', context)     # 返回使用者已購買課程頁面

8. 視訊傳輸許可權與協議

8.1 視訊播放頁面檢視函式

# 視訊播放頁面檢視函式
def video_handler(request, course_id):
    context = request.context
    try:
        course = Course.objects.get(id=course_id)
        session_user = request.session['session_user']  # 獲取登入使用者
        # 通過course_id判斷該使用者是否已經購買該課程
        boolean_buyed = User.objects.filter(id=session_user.get('id'), userBuyer_set__id=course_id).exists()
        if boolean_buyed:
            context['course'] = course
            return render(request, 'video.html', context)   # 如果已經購買課程, 直接返回到視訊播放頁面
        else:
            return redirect(reverse('course_course', args=(course_id, )))   # 否則返回到課程詳細頁
    except:
        return HttpResponse(status=404)     # 如果找不到課程, 返回404頁面

8.2 視訊流檢視函式(更安全,不需要直接把整個視訊件傳輸出去)

# 視訊流播放檢視函式
def videoStream_handler(request, course_id):
    # 定義讀取視訊流的方法
    def read_video(path):   # 傳入視訊路徑
        with open(path, 'rb') as f: # 以二進位制讀取方式開啟視訊檔案
            while True:
                data = f.read(1024*10)  # 獲取視訊檔案內容, 每次讀取?
                if data:
                    yield data          # 遍歷獲取視訊內容, 生成視訊內容, 直到視訊結束
                else:
                    break
    context = request.context
    try:
        course = Course.objects.get(id=course_id)       # 獲取課程
        session_user = request.session['session_user']  # 獲取登入客戶
        # 判斷使用者是否已購買該課程
        boolean_buyed = User.objects.filter(id=session_user.get('id'), userBuyer_set__id=course_id).exists()
        if boolean_buyed:   # 如果已購買, 通過之前讀取視訊流的方法, 返回視訊流播放介面
            context['course'] = course
            # 例項化response物件, 呼叫讀取視訊流的方法
            response = StreamingHttpResponse(read_video(course.fileName.__str__()), status=206)
            bytes_max = 1024 * 1024 * 2     # 限定2M大小
            # 設定視訊流物件的引數, 固定用法?
            response['Content-Range'] = 'bytes 0-102400/%s' % (bytes_max, os.path.getsize(course.fileName.__str__()))
            return response
        else:
            return redirect(reverse('course_course', args=(course_id, )))   # 否則返回到課程詳細頁
    except:
        return HttpResponse(status=404)     # 如果找不到課程, 返回404頁面

9.網站安全性優化

  • 通過中介軟體設定,對特定url地址的訪問作出限制,給出相應提示
from django.utils.deprecation import MiddlewareMixin
from course import views as course_views
from django.shortcuts import reverse
class MyMiddleware(MiddlewareMixin):
    def __init__(self, get_response=None):
        super().__init__(get_response)
        # 初始化中介軟體
        print('Initiating MyMiddleWare...')
    def process_request(self, request):
        # request.context = {}
        # # 如果沒有session_user, 直接返回None
        # session_user = None
        # # 如果session_user已經存在, 則把session_user封裝到context中
        # if 'session_user' in request.session.keys():
        #     session_user = request.context['session_user'] = request.session['session_user']
        # # 如果沒有session_user, 使用者想訪問視訊頁面或登入頁面的時候, 提示使用者先登入
        # if not session_user:
        #     if request.path.startswith('/video') or request.path.startswith('/user'):
        #         # 通過判斷賬號和密碼是否已經存在, 把登入介面讓出來, 避免所有的登入和註冊都被阻攔
        #         if request.path not in [reverse('user_login'), reverse('user_register')]:
        #             request.context['login_message'] = '請先登入!'
        #             return course_views.index_handler(request)
        # 通過dict()函式簡化第13行到16行的程式碼
        request.context = dict(
            session_user=request.session['session_user'] if 'session_user' in request.session.keys() else None
        )
        # 通過並列條件判斷, 合併第17行到19行的條件巢狀
        if (not request.context['session_user']) \
                and (request.path.startswith('/video') or request.path.startswith('/user')) \
                and (request.path not in [reverse('user_login'), reverse('user_register')]):
            request.context['login_message'] = '請先登入!'
            return course_views.index_handler(request)
    def process_response(self, request, response):
        print('Processing Response...')
        # 必須return response, 否則會報錯
        return response

相關文章