一、公开课列表页
- 将课程相关页面拷贝到项目 templates/ 目录下
- 课程列表页最终实现效果图
- 回顾 课程 model 模型类
# apps/courses/models.py
class Course(models.Model):
"""
课程表
"""
cj = "cj"
zj = "zj"
gj = "gj"
degree_choices = (
(cj, "初级"),
(zj, "中级"),
(gj, "高级"),
)
course_org = models.ForeignKey(CourseOrg, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="课程机构")
name = models.CharField(max_length=50, verbose_name="课程名称")
describe = models.CharField(max_length=200, verbose_name="课程描述")
detail = models.TextField(verbose_name="课程详情")
degree = models.CharField(max_length=2, choices=degree_choices, verbose_name="课程难度")
learn_times = models.IntegerField(default=0, verbose_name="学习时长(分钟数)")
students = models.IntegerField(default=0, verbose_name="学习人数")
favorite_nums = models.IntegerField(default=0, verbose_name="收藏人数")
image = models.ImageField(max_length=100, upload_to="courses/%Y/%m", verbose_name="封面图")
click_nums = models.IntegerField(default=0, verbose_name="点击数")
add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")
class Meta:
verbose_name = "课程"
verbose_name_plural = verbose_name
def __str__(self):
return self.name
- 定义 CourseListView 处理课程列表页逻辑
# apps/courses/views.py
from django.shortcuts import render
from django.views.generic.base import View
from pure_pagination import Paginator, EmptyPage, PageNotAnInteger
from .models import Course
class CourseListView(View):
def get(self, request):
# 所有课程
all_courses = Course.objects.all().order_by("-add_time")
# 课程总数量
course_nums = Course.objects.count()
# 热门课程
hot_courses = Course.objects.all().order_by("-click_nums")[:3]
# 课程排序
sort = request.GET.get("sort", "")
if sort:
if sort == "hot":
all_courses = all_courses.order_by("-click_nums")
elif sort == "students":
all_courses = all_courses.order_by("-students")
# 对课程进行分页
try:
page = request.GET.get("page", 1)
except PageNotAnInteger:
page = 1
p = Paginator(all_courses, 3, request=request)
courses = p.page(page)
context = {
"all_courses": courses,
"course_nums": course_nums,
"hot_courses": hot_courses,
"sort": sort,
}
return render(request, "course-list.html", context)
- 在 course-list.html 中接收并处理 CourseListView 返回到前端的数据(course-list.html 继承 base.html)
# templates/course-list.html
{% block content %}
<section>
<div class="wp">
<div class="list" style="margin-top:0;">
<div class="left layout">
<div class="head">
<ul class="tab_header">
<li {% ifequal sort "" %}class="active"{% endifequal %}><a href="?sort=">最新 </a></li>
<li {% ifequal sort "hot" %}class="active"{% endifequal %}><a href="?sort=hot">最热门</a></li>
<li {% ifequal sort "students" %}class="active"{% endifequal %}><a href="?sort=students">参与人数</a></li>
</ul>
</div>
<div id="inWindow">
<div class="tab_cont " id="content">
<div class="group_list">
{% for course in all_courses.object_list %}
<div class="box">
<a href="course-detail.html">
<img width="280" height="350" class="scrollLoading" src="{{ MEDIA_URL }}{{ course.image }}"/>
</a>
<div class="des">
<a href="course-detail.html">
<h2>{{ course.name }}</h2>
</a>
<span class="fl">时长:<i class="key">{{ course.learn_times }}</i></span>
<span class="fr">学习人数:{{ course.students }}</span>
</div>
<div class="bottom">
<a href="course-detail.html"><span class="fl">来自{{ course.course_org }}</span></a>
<span class="star fr notlogin" data-favid="15">{{ course.favorite_nums }}</span>
</div>
</div>
{% endfor %}
</div>
<div class="pageturn">
<ul class="pagelist">
{% if all_courses.has_previous %}
<li class="long"><a href="?{{ all_courses.previous_page_number.querystring }}">上一页</a></li>
{% endif %}
{% for page in all_courses.pages %}
{% if page %}
{% ifequal page all_courses.number %}
<li class="page active"><a href="?{{ page.querystring }}">{{ page }}</a></li>
{% else %}
<li><a href="?{{ page.querystring }}" class="page">{{ page }}</a></li>
{% endifequal %}
{% else %}
<li class="none"><a href="">...</a></li>
{% endif %}
{% endfor %}
{% if all_courses.has_next %}
<li class="long"><a href="?{{ all_courses.next_page_number.querystring }}">下一页</a></li>
{% endif %}
</ul>
</div>
</div>
</div>
</div>
<div class="right layout">
<div class="head">热门课程推荐</div>
<div class="group_recommend">
{% for hot_course in hot_courses %}
<dl>
<dt>
<a target="_blank" href="">
<img width="240" height="220" class="scrollLoading" src="{{ MEDIA_URL }}{{ hot_course.image }}"/>
</a>
</dt>
<dd>
<a target="_blank" href=""><h2>{{ hot_course.name }}</h2></a>
<span class="fl">难度:<i class="key">{{ hot_course.get_degree_display }}</i></span>
</dd>
</dl>
{% endfor %}
</div>
</div>
</div>
</div>
</section>
{% endblock content %}
# course-list.html 模板中代码有一个知识点
# 显示课程难度是通过 {{ hot_course.get_degree_display }} 变量实现的
# 这是 django 对 choices 字段的固定用法,如果直接写成 {{ hot_course.degree }}
#只能得到 "cj/zj/gz",而不能得到 "初级/中级/高级"
<span class="fl">难度:<i class="key">{{ hot_course.get_degree_display }}</i></span>
004.png如果直接写成 {{ hot_course.degree }}, 前端页面显示结果
005.png如果写成 {{ hot_course.get_degree_display }}, 前端页面显示结果
- 配置课程页面 url,首先要在 apps/courses/ 目录下新建 urls.py 文件
# mxonline/urls.py
...
urlpatterns = [
...
# 课程相关页面 url 配置
path("course/", include("courses.urls")),
]
# apps/courses/urls.py
from django.urls import path, re_path
from .views import CourseListView
app_name = "course"
urlpatterns = [
# 课程列表页
path("list/", CourseListView.as_view(), name="course_list"),
]
003.png
二、公开课详情页面
- 实现效果
- 定义 CourseDetailView 类视图处理课程详情页逻辑
# apps/courses/views.py
class CourseDetailView(View):
"""
课程详情页类视图
"""
def get(self, request, id):
course = Course.objects.get(pk=id)
# 每请求一次页面, 课程点击数加一
course.click_nums += 1
course.save()
# relate_courses 传递到前端页面, 作为 "相关课程推荐" 的课程
# 这里 "相关课程推荐" 是通过查找具有相同 "tag" 的课程, 取出其
# 中一个课程传递到前端, 如果没有与当前课程具有相同 "tag" 的课程,
# 则传递给前端一个空列表 []
tag = course.tag
if tag:
relate_courses = Course.objects.filter(tag=tag)[:1]
else:
relate_courses = []
context = {
"course": course,
"relate_courses": relate_courses,
}
return render(request, "course-detail.html", context)
- 配置 url
# apps/courses/urls.py
from django.urls import path, re_path
from .views import CourseListView, CourseDetailView
app_name = "course"
urlpatterns = [
...
# 课程详情页
path("detail/<int:id>/", CourseDetailView.as_view(), name="course_detail"),
]
- 在 course-detail.html 中接收并处理 CourseListView 返回到前端的数据
- (1)处理并显示课程相关数据
# templates/course-detail.html
<div class="left">
<div class="picbox">
<div class="tb-booth tb-pic">
<img width="440" height="445" src="{{ MEDIA_URL }}{{ course.image }}" class="jqzoom" />
</div>
</div>
<div class="des">
<h1 title="{{ course.name }}">{{ course.name }}</h1>
<span class="key">{{ course.describe }}</span>
<div class="prize">
<span class="fl">难度:<i class="key">{{ course.get_degree_display }}</i></span>
<span class="fr">学习人数:{{ course.students }}</span>
</div>
<ul class="parameter">
<li><span class="pram word3">时 长:</span><span>{{ course.learn_times }}</span></li>
<li><span class="pram word3">章 节 数:</span><span>{{ course.lesson_set.count }}</span></li>
<li><span class="pram word3">课程类别:</span><span title="{{ course.category }}">{{ course.category }}</span></li>
<li class="piclist"><span class="pram word4">学习用户:</span>
{% for learn_user in course.get_learn_users %}
<span class="pic"><img width="40" height="40" src="{{ MEDIA_URL }}{{ learn_user.user.image }}"/></span>
{% endfor %}
</li>
</ul>
<div class="btns">
<div class="btn colectgroupbtn" id="jsLeftBtn">
收藏
</div>
<div class="buy btn"><a style="color: white" href="course-video.html">开始学习</a></div>
</div>
</div>
</div>
- 其中 get_learn_users 方法是在 Course 中定义的,用来获取课程学习用户并在页面中显示用户头像
# apps/courses/models.py
class Course(models.Model):
"""
课程表
"""
...
def get_learn_users(self):
# 获取课程学习用户
return self.usercourse_set.all()[:5]
- (2)简单处理课程详情,后期课程详情部分是可以在后台利用富文本编辑的
# templates/course-detail.html
<div class="head">
<ul class="tab_header">
<li class="active">课程详情</li>
</ul>
</div>
<div class="tab_cont tab_cont1">
<p>{{ course.detail }}</p>
</div>
- (3)处理授课机构部分
# templates/course-detail.html
<div class="right">
<div class="head">
<h1>授课机构</h1>
<p>世界名校,课程权威</p>
</div>
<div class="pic">
<a href="/company/14/">
<img width="150" height="80" src="{{ MEDIA_URL }}{{ course.course_org.image }}"/>
</a>
</div>
<a href="/company/14/">
<h2 class="center" title="{{ course.course_org.name }}">{{ course.course_org.name }}</h2>
</a>
<div class="btn notlogin
"data-favid="14" id="jsRightBtn">
已收藏
</div>
<div class="clear">
<ul>
<li>
<span>课 程 数: {{ course.course_org.course_nums }}</span>
</li>
<li>
<span>教 师 数: {{ course.course_org.teacher_set.count }}</span>
</li>
<li>所在地区: {{ course.course_org.address }}</li>
<li>认 证 :
<img title="金牌机构" src="{% static "images/gold.png" %}"/>
</li>
</ul>
</div>
</div>
- (4)处理相关课程推荐
# templates/course-detail.html
<div class="right layout">
<div class="head">相关课程推荐</div>
<div class="group_recommend">
{% for relate_course in relate_courses %}
<dl>
<dt>
<a target="_blank" href="{% url "course:course_detail" relate_course.id %}">
<img width="240" height="220" class="scrollLoading" src="{{ MEDIA_URL }}{{ relate_course.image }}"/>
</a>
</dt>
<dd>
<a target="_blank" href="{% url "course:course_detail" relate_course.id %}"><h2> {{ relate_course.name }}</h2></a>
<span class="fl">学习时长:<i class="key">{{ relate_course.learn_times }}</i></span>
</dd>
</dl>
{% empty %}
<dl>
<dt>暂时还没有相关课程</dt>
</dl>
{% endfor %}
</div>
</div>
- (5)处理课程和课程机构的收藏功能
-
参考 第7章 课程机构收藏的代码,这里添加、删除收藏也是用的同一个类视图 AddFavoriteView 来处理
-
修改 CourseDetailView 类视图
# apps/courses/views.py
class CourseDetailView(View):
"""
课程详情页类视图
"""
def get(self, request, id):
course = Course.objects.get(pk=id)
# 每请求一次页面, 课程点击数加一
course.click_nums += 1
course.save()
# relate_courses 传递到前端页面, 作为 "相关课程推荐" 的课程,
# 这里 "相关课程推荐" 是通过查找具有相同 "tag" 的课程, 取出其
# 中一个课程传递到前端, 如果没有与当前课程具有相同 "tag" 的课程,
# 则传递给前端一个空列表 []
tag = course.tag
if tag:
relate_courses = Course.objects.filter(tag=tag)[:1]
else:
relate_courses = []
# 用来传递到前端页面, 判断用户是否已收藏课程或者课程所在机构
has_favorite_course = False
has_favorite_course_org = False
if request.user.is_authenticated:
if UserFavorite.objects.filter(user=request.user, favorite_id=course.id, favorite_type=1):
has_favorite_course = True
if UserFavorite.objects.filter(user=request.user, favorite_id=course.course_org_id, favorite_type=2):
has_favorite_course_org = True
context = {
"course": course,
"relate_courses": relate_courses,
"has_favorite_course": has_favorite_course,
"has_favorite_course_org": has_favorite_course_org,
}
return render(request, "course-detail.html", context)
- 前端页面 ajax 部分代码
# templates/course-detail.html
{% block custom_js %}
<script type="text/javascript">
//收藏分享
function add_favorite(current_elem, favorite_id, favorite_type){
$.ajax({
cache: false,
type: "POST",
url:"{% url "org:add_favorite" %}",
data:{'favorite_id':favorite_id, 'favorite_type':favorite_type},
async: true,
// 因为用户点击"收藏"按钮后,向后台提交 POST 请求时,没有通过 form,
// 不能在 form 中定义 {% csrf_token %}, 所以只能在这里把 {{ csrf_token }} 传递进来
beforeSend:function(xhr, settings){
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
},
success: function(data) {
var data = JSON.parse(data);
if(data.status == "fail"){
if(data.msg == "用户未登录"){
window.location.href="{% url "login" %}";
}else{
alert(data.msg);
}
}else if(data.status == 'success'){
current_elem.text(data.msg);
}
},
});
}
// 监听事件, 用来监听收藏按钮的点击操作
$('#jsLeftBtn').on('click', function(){
add_favorite($(this), {{ course.id }}, 1);
});
$('#jsRightBtn').on('click', function(){
add_favorite($(this), {{ course.course_org.id }}, 2);
});
</script>
{% endblock %}
- 处理前端页面 "收藏" 按钮应该显示的文字部分逻辑
# templates/course-detail.html
<div class="btn colectgroupbtn" id="jsLeftBtn">
{% if has_favorite_course %}已收藏{% else %}收藏{% endif %}
</div>
...
<div class="btn notlogin"data-favid="14" id="jsRightBtn">
{% if has_favorite_course_org %}已收藏{% else %}收藏{% endif %}
</div>
三、课程章节信息页面
- 用户在公开课详情页面点击"开始学习"按钮后,是要跳转到课程章节信息页面
- 观察页面发现实际上是有两个页面,一个是章节的信息,另一个是评论的信息,两个页面分别是"course-video.html"和"course-comment.html",将两个页面复制到 template 目录下
- 首先完成 "course-video.html" 章节信息页面:
- (1)观察页面发现页面header 和 footer 部分是公共的,可以继承 "base.html"
# templates/course-video.html
...
# 面包屑导航部分
{% block custom_bread %}
<section>
<div class="wp">
<div class="crumbs">
<ul>
<li><a href="{% url "index" %}">首页</a>></li>
<li><a href="{% url "course:course_list" %}">公开课程</a>></li>
<li><a href="{% url "course:course_detail" course.id %}">课程详情</a>></li>
<li>章节信息</li>
</ul>
</div>
</div>
</section>
{% endblock custom_bread %}
- (2)定义课程章节信息页类视图 CourseInfoView.
# apps/courses/views.py
class CourseInfoView(View):
"""
课程章节信息页类视图
"""
def get(self, request, id):
course = Course.objects.get(pk=id)
course_resources = CourseResource.objects.filter(course=course)
context = {
"course": course,
"course_resources": course_resources,
}
return render(request, "course-video.html", context)
- (3)配置 url
# apps/courses/urls.py
from .views import CourseInfoView
app_name = "course"
urlpatterns = [
...
# 课程章节信息页
path("info/<int:id>/", CourseInfoView.as_view(), name="course_info"),
]
- (4)回顾课程章节和课程视频 model 类
# apps/courses/models.py
class Course(models.Model):
"""
课程表
"""
cj = "cj"
zj = "zj"
gj = "gj"
degree_choices = (
(cj, "初级"),
(zj, "中级"),
(gj, "高级"),
)
course_org = models.ForeignKey(CourseOrg, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="课程机构")
name = models.CharField(max_length=50, verbose_name="课程名称")
describe = models.CharField(max_length=200, verbose_name="课程描述")
detail = models.TextField(verbose_name="课程详情")
degree = models.CharField(max_length=2, choices=degree_choices, verbose_name="课程难度")
learn_times = models.IntegerField(default=0, verbose_name="学习时长(分钟数)")
teacher = models.ForeignKey(Teacher, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="课程讲师")
students = models.IntegerField(default=0, verbose_name="学习人数")
favorite_nums = models.IntegerField(default=0, verbose_name="收藏人数")
image = models.ImageField(max_length=100, upload_to="courses/%Y/%m", verbose_name="封面图")
category = models.CharField(max_length=20, default="后端开发", verbose_name="课程类别")
tag = models.CharField(max_length=20, default="", verbose_name="课程标签")
need_know = models.CharField(max_length=200, default="", verbose_name="课程须知")
teacher_tell = models.CharField(max_length=200, default="", verbose_name="老师告诉你")
click_nums = models.IntegerField(default=0, verbose_name="点击数")
add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")
class Meta:
verbose_name = "课程"
verbose_name_plural = verbose_name
def __str__(self):
return self.name
def get_learn_users(self):
# 获取课程学习用户
return self.usercourse_set.all()[:5]
def get_course_lessons(self):
# 获取课程所有章节
return self.lesson_set.all()
class Lesson(models.Model):
"""
课程章节表
"""
course = models.ForeignKey(Course, on_delete=models.SET_NULL, null=True, verbose_name="课程名称")
name = models.CharField(max_length=50, verbose_name="章节名称")
add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")
class Meta:
verbose_name = "课程章节"
verbose_name_plural = verbose_name
def __str__(self):
return self.name
def get_lesson_videos(self):
# 获取章节所有视频
return self.video_set.all()
class Video(models.Model):
"""
课程视频表
"""
lesson = models.ForeignKey(Lesson, on_delete=models.SET_NULL, null=True, verbose_name="章节名称")
name = models.CharField(max_length=50, verbose_name="视频名称")
url = models.URLField(max_length=200, default="", verbose_name="访问地址")
learn_times = models.IntegerField(default=0, verbose_name="学习时长(分钟数)")
add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")
class Meta:
verbose_name = "课程视频"
verbose_name_plural = verbose_name
def __str__(self):
return self.name
- (5)课程章节部分功能实现
# templates/course-video.html
<div class="content">
<div class="mod-tab-menu">
<ul class="course-menu clearfix">
<li><a class="ui-tabs-active active" id="learnOn" href="{% url "course:course_info" course.id %}"><span>章节</span></a></li>
<li><a id="commentOn" class="" href="{% url "course:course_comment" course.id %}"><span>评论</span></a></li>
</ul>
</div>
<div id="notice" class="clearfix">
<div class="l"> <strong>课程公告:</strong> <a href="javascript:void(0)">Spring的文档以及相关的jar文件已上传</a> </div>
</div>
<div class="mod-chapters">
{% for lesson in course.get_course_lessons %}
<div class="chapter chapter-active" >
<h3>
<strong><i class="state-expand"></i><a target="_blank" href="{{ lesson.url }}">{{ lesson.name }}</strong>
</h3>
<ul class="video">
{% for video in lesson.get_lesson_videos %}
<li>
<a target="_blank" href='/video/3662' class="J-media-item studyvideo">{{ video.name }}({{ video.learn_times }})
<i class="study-state"></i>
</a>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
</div>
</div>
- (6)资料下载
# templates/course-video.html
<div class="box mb40">
<h4>资料下载</h4>
<ul class="downlist">
{% for course_resource in course_resources %}
<li>
<span ><i class="aui-iconfont aui-icon-file"></i> {{ course_resource.name }}</span>
<a href="{{ MEDIA_URL }}{{ course_resource.download }}" class="downcode" target="_blank" download="" data-id="274" title="">下载</a>
</li>
{% endfor %}
</ul>
</div>
- (7)课程基本信息展示
# templates/course-video.html
<div class="w pr">
<div style="height: 15px" class="path">
</div>
<div class="hd">
<h2 class="l">{{ course.name }}</h2>
</div>
<div class="statics clearfix">
<div class="static-item ">
<span class="meta-value"><strong>{{ course.get_degree_display }}</strong></span>
<span class="meta">难度</span>
<em></em>
</div>
<div class="static-item static-time">
<span class="meta-value">{{ course.learn_times }}分钟</span>
<span class="meta">时长</span>
<em></em>
</div>
<div class="static-item">
<span class="meta-value"><strong>{{ course.students }}</strong></span>
<span class="meta">学习人数</span>
<em></em>
</div>
</div>
</div>
- (8)讲师提示
# templates/course-video.html
<div class="box mb40">
<h4>讲师提示</h4>
<div class="teacher-info">
<a href="/u/315464/courses?sort=publish" target="_blank">
<img src='{{ MEDIA_URL }}{{ course.teacher.image }}' width='80' height='80' />
</a>
<span class="tit">
<a href="/u/315464/courses?sort=publish" target="_blank">{{ course.teacher.name }}</a>
</span>
<span class="job">{{ course.teacher.work_position }}</span>
</div>
<div class="course-info-tip">
<dl class="first">
<dt>课程须知</dt>
<dd class="autowrap">{{ course.need_know }}</dd>
</dl>
<dl>
<dt>老师告诉你能学到什么?</dt>
<dd class="autowrap">{{ course.teacher_tell }}</dd>
</dl>
</div>
</div>
- 课程评论页面 "course-comment.html"
- (1)定义课程评论页类视图 CourseCommentsView
# apps/courses/views.py
class CourseCommentsView(View):
"""
课程评论页类视图
"""
def get(self, request, id):
course = Course.objects.get(pk=id)
course_resources = CourseResource.objects.filter(course=course)
course_comments = CourseComments.objects.filter(course=id)
context = {
"course": course,
"course_resources": course_resources,
"course_comments": course_comments,
}
return render(request, "course-comment.html", context)
- (2)配置 url
# apps/courses/urls.py
from django.urls import path, re_path
from .views import CourseCommentsView
app_name = "course"
urlpatterns = [
...
# 课程评论页
path("comment/<int:id>", CourseCommentsView.as_view(), name="course_comment"),
]
- (3)发表评论功能
发表评论功能是通过 ajax 向后台发送请求, 如果评论添加成功, 刷新评论部分局部页面, 重新加载该课程下面全部评论
019.png定义用户添加课程评论类视图 AddCommentsView
# apps/courses/views.py
class AddCommentsView(View):
"""
用户添加课程评论类视图
"""
def post(self, request):
# 当用户点击"发表评论"按钮时, 要判断用户是否处于登录状态
if not request.user.is_authenticated:
# 用户未登录, 返回错误信息
return JsonResponse(data='{"status": "fail", "msg": "用户未登录"}', safe=False)
# 验证用户已经登录, 执行以下逻辑, 保存用户发表的评论信息
course_id = request.POST.get("course_id", 0)
comments = request.POST.get("comments", "")
if int(course_id) > 0 and comments:
# 如果课程 id 大于 0, 并且课程评论不为空,
# 才像数据库中写入用户评论信息数据
course_comments = CourseComments()
course = Course.objects.get(pk=int(course_id))
course_comments.course = course
course_comments.comments = comments
# 不要忘记保存评论信息对应的用户
course_comments.user = request.user
course_comments.save()
return JsonResponse(data='{"status": "success", "msg": "添加成功"}', safe=False)
else:
return JsonResponse(data='{"status": "fail", "msg": "添加失败"}', safe=False)
配置 url
# apps/courses/urls.py
from django.urls import path, re_path
from .views import AddCommentsView
app_name = "course"
urlpatterns = [
...
# 添加课程评论
path("add_comment/", AddCommentsView.as_view(), name="add_comment"),
]
前端页面 ajax 部分代码
# templates/course-comment.html
{% block custom_js %}
<script type="text/javascript">
//添加评论
$('#js-pl-submit').on('click', function(){
var comments = $("#js-pl-textarea").val()
if(comments == ""){
alert("评论不能为空")
return
}
$.ajax({
cache: false,
type: "POST",
url:"{% url "course:add_comment" %}",
data:{'course_id':{{ course.id }}, 'comments':comments},
async: true,
beforeSend:function(xhr, settings){
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
},
success: function(data) {
var data = JSON.parse(data);
if(data.status == 'fail'){
if(data.msg == '用户未登录'){
window.location.href="login.html";
}else{
alert(data.msg)
}
}else if(data.status == 'success'){
window.location.reload();//刷新当前页面.
}
},
});
});
</script>
{% endblock custom_js %}
前端页面显示评论信息部分代码
# templates/course-comment.html
<!--发布评论-->
<div id="js-pub-container" class="issques clearfix js-form">
<div class="wgt-ipt-wrap pub-editor-wrap " id="js-pl-input-fake">
<textarea id="js-pl-textarea" class="" placeholder="扯淡、吐槽、表扬、鼓励……想说啥就说啥!" ></textarea>
</div>
<input type="button" id="js-pl-submit" class="pub-btn" data-cid="452" value="发表评论">
<p class="global-errortip js-global-error"></p>
</div>
<div id="course_note">
<ul class="mod-post" id="comment-list">
{% for course_comment in course_comments %}
<li class="post-row">
<div class="media">
<span target="_blank"><img src='{{ MEDIA_URL }}{{ course_comment.user.image }}' width='40' height='40' /></span>
</div>
<div class="bd">
<div class="tit">
<span target="_blank">{{ course_comment.user.username }}</span>
</div>
<p class="cnt">{{ course_comment.comments }}</p>
<div class="footer clearfix">
<span title="创建时间" class="l timeago">时间:{{ course_comment.add_time }}</span>
</div>
</div>
</li>
{% endfor %}
</ul>
</div>
-
该课的同学还学过
020.png
- (1)完善 CourseInfoView 和 CourseCommentsView 类视图
# apps/courses/views.py
class CourseInfoView(LoginRequiredMixin, View):
"""
课程章节信息页类视图
此视图继承 LoginRequiredMixin 是为了验证用户是否登录
"""
def get(self, request, id):
course = Course.objects.get(pk=id)
course_resources = CourseResource.objects.filter(course=course)
# 当用户点击 "开始学习" 按钮后, 是要将用户和该课程进行关联的,
# 这样, 才能将用户学习课程记录到 UserCourse 表中
# 查询用户是否已经关联了该课程
user_courses = UserCourse.objects.filter(user=request.user, course=course)
if not user_courses:
# 如果用户没有关联该课程, 则进行关联
user_course = UserCourse(user=request.user, course=course)
user_course.save()
# 获取 UserCourse 表中所有学习该课程的记录
user_courses = UserCourse.objects.filter(course=course)
# 获取学习该课程的全部用户 id 列表
user_ids = [user_course.user.id for user_course in user_courses]
# 获取学习该课程的全部用户的所有学习课程记录
# 这里 "user_id__in", 是 django 的用法, 可以遍历 user_ids 中的每一个用户
all_user_courses = UserCourse.objects.filter(user_id__in=user_ids)
# 获取学习该课程的全部用户的所有学习课程的课程 id 列表
course_ids = [user_course.course.id for user_course in all_user_courses]
# 学过该课的同学还学过的课程
relate_courses = Course.objects.filter(id__in=course_ids).order_by("-click_nums")[:5]
context = {
"course": course,
"course_resources": course_resources,
"relate_courses": relate_courses,
}
return render(request, "course-video.html", context)
class CourseCommentsView(LoginRequiredMixin, View):
"""
课程评论页类视图
此视图继承 LoginRequiredMixin 是为了验证用户是否登录
"""
def get(self, request, id):
course = Course.objects.get(pk=id)
course_resources = CourseResource.objects.filter(course=course)
course_comments = CourseComments.objects.filter(course=id)
# 当用户点击 "开始学习" 按钮后, 是要将用户和该课程进行关联的,
# 这样, 才能将用户学习课程记录到 UserCourse 表中
# 查询用户是否已经关联了该课程
user_courses = UserCourse.objects.filter(user=request.user, course=course)
if not user_courses:
# 如果用户没有关联该课程, 则进行关联
user_course = UserCourse(user=request.user, course=course)
user_course.save()
# 获取 UserCourse 表中所有学习该课程的记录
user_courses = UserCourse.objects.filter(course=course)
# 获取学习该课程的全部用户 id 列表
user_ids = [user_course.user.id for user_course in user_courses]
# 获取学习该课程的全部用户的所有学习课程记录
# 这里 "user_id__in", 是 django 的用法, 可以遍历 user_ids 中的每一个用户
all_user_courses = UserCourse.objects.filter(user_id__in=user_ids)
# 获取学习该课程的全部用户的所有学习课程的课程 id 列表
course_ids = [user_course.course.id for user_course in all_user_courses]
# 学过该课的同学还学过的课程
relate_courses = Course.objects.filter(id__in=course_ids).order_by("-click_nums")[:5]
context = {
"course": course,
"course_resources": course_resources,
"course_comments": course_comments,
"relate_courses": relate_courses,
}
return render(request, "course-comment.html", context)
其中 LoginRequiredMixin 是为了验证用户是否登录而自定义的类视图
021.png
- (2)前端页面代码
# templates/course-comment.html / templates/course-video.html
<div class="cp-other-learned js-comp-tabs">
<div class="cp-header clearfix">
<h2 class="cp-tit l">该课的同学还学过</h2>
</div>
<div class="cp-body">
<div class="cp-tab-pannel js-comp-tab-pannel" data-pannel="course" style="display: block">
<!-- img 200 x 112 -->
<ul class="other-list">
{% for relate_course in relate_courses %}
<li class="curr">
<a href="{% url "course:course_detail" relate_course.id %}" target="_blank">
<img src="{{ MEDIA_URL }}{{ relate_course.image }}" alt="{{ relate_course.name }}">
<span class="name autowrap">{{ relate_course.name }}</span>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
-
课程播放页面
在章节信息页面,点击视频标题,新打开一个窗口,课程播放页面,页面除了上部播放视频的窗口,其他地方和章节信息页面展示的信息是相同的
022.png
- (1)定义 VideoPlayView 类视图,用来处理视频播放
# apps/courses/views.py
class VideoPlayView(View):
"""
播放课程视频类视图
"""
def get(self, request, id):
video = Video.objects.get(pk=id)
course = video.lesson.course
course_resources = CourseResource.objects.filter(course=course)
# 当用户点击 "开始学习" 按钮后, 是要将用户和该课程进行关联的,
# 这样, 才能将用户学习课程记录到 UserCourse 表中
# 查询用户是否已经关联了该课程
user_courses = UserCourse.objects.filter(user=request.user, course=course)
if not user_courses:
# 如果用户没有关联该课程, 则进行关联
user_course = UserCourse(user=request.user, course=course)
user_course.save()
# 获取 UserCourse 表中所有学习该课程的记录
user_courses = UserCourse.objects.filter(course=course)
# 获取学习该课程的全部用户 id 列表
user_ids = [user_course.user.id for user_course in user_courses]
# 获取学习该课程的全部用户的所有学习课程记录
# 这里 "user_id__in", 是 django 的用法, 可以遍历 user_ids 中的每一个用户
all_user_courses = UserCourse.objects.filter(user_id__in=user_ids)
# 获取学习该课程的全部用户的所有学习课程的课程 id 列表
course_ids = [user_course.course.id for user_course in all_user_courses]
# 学过该课的同学还学过的课程
relate_courses = Course.objects.filter(id__in=course_ids).order_by("-click_nums")[:5]
context = {
"course": course,
"course_resources": course_resources,
"relate_courses": relate_courses,
"video": video,
}
return render(request, "course-play.html", context)
- (2)配置 url
# apps/courses/urls.py
from django.urls import path, re_path
from .views import VideoPlayView
app_name = "course"
urlpatterns = [
...
# 播放视频页面
path("video/<int:id>/", VideoPlayView.as_view(), name="video_play"),
]
- (3)前端部分代码,播放视频功能采用的是开源库video.js,官网:http://videojs.com/,
024.png
# templates/course-play.html
<!--标题部分代码-->
{% block title %}{{ video.name }} - 慕学在线网{% endblock title %}
<!--css-->
{% block custom_css %}
...
<link href="http://vjs.zencdn.net/6.6.3/video-js.css" rel="stylesheet">
<style>
.video-js{
width: 1200px;
height: 675px;
}
.video-js .vjs-big-play-button{
top: 50%;
left: 50%;
}
</style>
{% endblock custom_css %}
<!--js-->
{% block custom_js %}
<script src="http://vjs.zencdn.net/6.6.3/video.js"></script>
{% endblock custom_js %}
<!--面包屑导航-->
{% block custom_bread %}
<section>
<div class="wp">
<div class="crumbs">
<ul>
<li><a href="{% url "index" %}">首页</a>></li>
<li><a href="{% url "course:course_list" %}">公开课程</a>></li>
<li><a href="{% url "course:course_detail" course.id %}">{{ course.name }}</a>></li>
<li>{{ video.name }}</li>
</ul>
</div>
</div>
</section>
{% endblock custom_bread %}
<!--视频播放 video 标签-->
{% block content %}
<div id="main">
<div style="width: 1200px; height: 675px; margin: 0 auto;">
<video id="example_video_1" class="video-js vjs-default-skin" controls preload="auto" width="1200" height="675" data-setup="{}">
<source src="{{ MEDIA_URL }}courses/video/test.mp4" type="video/mp4">
</video>
</div>
...
</div>
{% endblock content %}
网友评论