django实战项目课程平台搭建

一需求:

具备的功能:

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

发布了374 篇原创文章 · 获赞 1811 · 访问量 210万+

猜你喜欢

转载自blog.csdn.net/CSDNedu/article/details/103391873