课程视频管理功能,实现课程视频的增、删、改、查和图片上传到服务器功能功能的实现
一、分析
1. 业务流程
1. 课程视频展示
-
业务流程
- 根据提供信息进行课程视频查找(参照文章管理)
- 从后端查取数据返回到前端,进行页面填充
-
请求方式、地址、参数
-
请求方式:GET
-
请求地址:/admin/course/
-
权限:view_course
-
请求参数:
参数名 类型 是否必传 备注 start_time datatime 不是必传 form表单参数 end_time datatime 不是必传 form表单参数 title char 不是必传 form表单参数 teacher char 不是必传 form表单参数 cate_id int 不是必传 form表单参数
-
2. 课程视频删除功能
-
业务流程
- 从前端传递一个带有视频id的delete请求
- 判断数据库中是否有该数据
- 逻辑删除该课程视频,并返回数据到前端
-
请求方式、地址、参数
-
请求方式:DELETE
-
请求地址:/admin/course/edit/<int:course_id>/
-
权限:delete_course
-
请求参数:
参数名 类型 是否必传 备注 course_id int 必传 路径参数
-
3. 课程视频修改功能
-
业务流程
- 判断课程视频名、课程视频简介、课程视频封面图、课程视频分类、课程视频url、课程大纲是否为空
- 判断数据库中是否存在id=doc_id的数据
- 进行表单校验
- 成功后将数据保存进数据库
- 将数据返回到前端
-
请求方式、地址、参数
-
请求方式:PUT
-
请求地址:/admin/course/edit/<int:course_id>/
-
权限:change_course
-
请求参数:
参数名 类型 是否必传 备注 course_id int 必传 路径参数 name char 必传 请求体里 brief char 必传 请求体里 category int 必传 路径参数 teacher int 必传 路径参数 cover_url url 必传 请求体里 video_url url 必传 请求体里 outline text 必传 请求体里
-
-
前端功能实
-
判断课程视频名、课程视频简介、课程视频封面图、课程视频分类、课程视频url、课程大纲是否为空
-
发起ajax PUT请求
- 跳转到课程视频管理页面
-
-
后端逻辑处理
- 接收前端传来的数据并转化为字典
- 通过form表单校验上面数据
- 校验成功后将数据保存进数据库
- 返回数据到前端
4. 课程视频发布功能
-
业务流程
- 判断课程视频名、课程视频简介、课程视频封面图、课程视频分类、课程视频url、课程大纲是否为空
- 进行表单校验
- 成功后将数据保存进数据库
- 将数据返回到前端
-
请求方式、地址、参数
-
请求方式:POST
-
请求地址:/admin/course/pub/
-
权限:add_course
-
请求参数:
参数名 类型 是否必传 备注 name char 必传 请求体里 brief char 必传 请求体里 category int 必传 路径参数 teacher int 必传 路径参数 cover_url url 必传 请求体里 video_url url 必传 请求体里 outline text 必传 请求体里
-
-
前端功能实
-
判断课程视频名、课程视频简介、课程视频封面图、课程视频分类、课程视频url、课程大纲是否为空
-
发起ajax POST请求
- 跳转到课程视频管理页面
-
-
后端逻辑处理
- 接收前端传来的数据并转化为字典
- 通过form表单校验上面数据
- 校验成功后将数据保存进数据库
- 返回数据到前端
5. 上传视频到阿里云
-
业务流程
-
构建一个百度云视频上传接口
-
判断课程名是否为空
-
判断课程简介是否为空
-
调用百度VOD接口上传视频
-
二、课程视频展示功能实现
1. urls配置
from django.urls import path
from admin import views
app_name = "admin"
urlpatterns = [
path("course/", views.CourseIndexView.as_view(), name="course"),
path("course/edit/<int:course_id>/", views.CourseEditView.as_view(), name="course_edit"),
path("course/pub/", views.CoursePubView.as_view(), name="course_pub"),
]
2. views视图逻辑处理
import json
from datetime import datetime
from urllib.parse import urlencode
from collections import OrderedDict # 转化为字典
from django.views import View
from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from news import models
from admin import contains
from settings import FDFS_URL
from course.models import Course, CourseCategory, Teacher
from users.models import Users
from utils.fast.fdfs import client
from admin.forms import ArticleEditForm, DocEditForm, CourseEditForm
from utils.page.per_page import get_page_data
from utils.res_code.res_code import Code, error_map
from utils.res_code.json_function import to_json_data
# 课程视频展示
class CourseIndexView(PermissionRequiredMixin, View):
"""
course video show page
route:/admin/course/
permissions:view_course
"""
permission_required = ("course.view_course",)
raise_exception = True
def get(self, request):
# 1. 从数据库中获取到文档的数据
course = Course.objects.select_related("teacher", "category").only("id", "name", "teacher__name",
"category__name", "update_time").filter(
is_delete=False).order_by("-update_time", "-id")
category = CourseCategory.objects.only("name").filter(is_delete=False)
# 2. 获取前端传来的数据:判断1个获取一个:start_time、end_time、doc_title、doc_author
# 判断起始时间start_time
try:
start_time = request.GET.get("start_time", "").strip()
start_time = datetime.strptime(start_time, "Y%m%d%")
except Exception as e:
# logger.info("起始时间格式错误:{}".format(e))
start_time = ""
# 判断结束时间end_time
try:
end_time = request.GET.get("end_time", "").strip()
end_time = datetime.strptime(end_time, "Y%m%d%")
except Exception as e:
# logger.info("起始时间格式错误:{}".format(e))
end_time = ""
# 判断起始时间和结束时间输入的三种情况:1.起始时间有、结束无;2.起始时间无、结束时间有;3.起始时间大于结束时间
# 起始时间有、结束无
if start_time and not end_time:
course = course.filter(update_time__lte=start_time)
# 起始时间无、结束时间有
if end_time and not start_time:
course = course.filter(update_time__gte=end_time)
# 起始时间大于结束时间
if start_time and end_time:
course = course.filter(update_time__range=(start_time, end_time))
if not course:
return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
# 3. 对文章标题进行判断/模糊查询
name = request.GET.get("name", "").strip()
if name:
course = course.filter(is_delete=False, name__icontains=name)
# 4. 对文章作者进行判断模糊查询
teacher = request.GET.get("teacher_name", "").strip()
if teacher:
course = course.filter(is_delete=False, teacher__name__icontains=teacher)
cate_id = int(request.GET.get("category", "0"))
if cate_id:
course = course.filter(is_delete=False, category=cate_id)
# 5. 进行分页处理
try:
page_num = int(request.GET.get("page", 1))
except Exception as e:
logger.info("页码格式错误:{}".format(e))
page_num = 1
page_obj = Paginator(course, contains.PER_PAGE_NUMBER)
try:
course_info = page_obj.page(page_num)
except EmptyPage: # 页码为空
course_info = page_obj.page(page_obj.num_pages)
pages_data = get_page_data(page_obj, course_info)
# 将时间转化为字符串
start_time = start_time.strftime("%Y%m%d") if start_time else ""
end_time = end_time.strftime("%Y%m%d") if end_time else ""
# 6. 将数据传递给前端
data = {
'course_info': course_info,
"categories": category,
'paginator': page_obj,
'start_time': start_time,
'end_time': end_time,
'name': name,
'teacher': teacher,
"cate_id": cate_id,
'other_param': urlencode({
'start_time': start_time,
'end_time': end_time,
'name': name,
'teacher': teacher,
"cate_id": cate_id,
})
}
data.update(pages_data)
return render(request, 'admin/course/course_index.html', context=data)
3. js前端逻辑处理
$(function () {
let $startTime = $("input[name=start_time]");
let $endTime = $("input[name=end_time]");
const config = {
autoclose: true,// 自动关闭
format: 'yyyy/mm/dd',// 日期格式
language: 'zh-CN',// 选择语言为中文
showButtonPanel: true,// 优化样式
todayHighlight: true, // 高亮今天
calendarWeeks: true,// 是否在周行的左侧显示周数
clearBtn: true,// 清除
startDate: new Date(1900, 10, 1),// 0 ~11 网站上线的时候
endDate: new Date(), // 今天
};
$startTime.datepicker(config);
$endTime.datepicker(config);
//文档删除
let $delBtn = $(".btn-del");
$delBtn.click(function () {
let _this = this;
let sCourseId = $(this).data("course-id");
fAlert.alertConfirm({
title: "确定删除该课程吗?",
type: "error",
confirmButtonText: "确认删除",
cancelButtonText: "取消",
confirmCallback: function confirmCallback() {
$.ajax({
url: "/admin/course/edit/" + sCourseId + '/',
type: "DELETE",
dataType: "json",
})
.done(function (res) {
if (res.errno === "200") {
message.showSuccess("成功删除课程!");
$(_this).parents("tr").remove()
} else {
swal.showInputError("删除失败:" + res.errmsg);
}
})
.fail(function () {
alert("服务器超时,请重试!")
})
}
})
});
// 获取cookie
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
let cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
let cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// 添加token到request中
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
});
4. html页面填充
{% extends 'admin/base/base.html' %}
{% load staticfiles %}
{% block title %}
课程管理页
{% endblock %}
{% block css %}
<link rel="stylesheet" href="{% static 'css/admin/news/bootstrap-datepicker.min.css' %}">
{% endblock %}
{% block content_header %}
课程管理
{% endblock %}
{% block content_header_brief %}
课程的增删改查
{% endblock %}
{% block content %}
<style>
.ml20 {
margin-left: 20px;
}
.mt20 {
margin-top: 20px;
}
</style