零基础使用Django2.0.1打造在线教育网站(十九):课程详
写在前面
本篇笔记我们将介绍课程详情页面的配置,这个页面和我们之前配置的页面差别不是很大,可以借鉴前面的配置。
本篇笔记对应于第十九篇代码,对应于github的位置是https://github.com/licheetools/eduline。
总结一下
小伙伴们可能会问了,这篇笔记还没开始写怎么就开始总结了呢?是的,尽管没写但是我觉得非常有必要在这里总结一下,特别是对之前讲的信息的总结。后面随着开发进度的加快,一些东西我就不会详细介绍了,因为都是非常类似的操作。
我们现在可以理解MTV的模式就应该是这样子的:新建数据库字段(models)-->配置访问路径(urls) -->新建视图函数(views) --> 页面信息动态加载(templates),所以后面的操作就是这个过程,大家在学习的时候对此要有一个清醒的认识。
话不多说,我们正式进入今天的学习内容,走你。
课程列表页配置
老规矩,把前端资料里面的course-list.html页面拷贝到我们的templates文件夹里面,接着配置url,在eduline/urls.py文件新增如下代码:
# 课程相关应用path配置
path("course/", include('courses.urls', namespace="course")),
然后打开courses应用,在里面新建urls.py文件并在里面新增如下代码:
from django.urls import path, include, re_path
from .views import CourseListView
app_name = "courses"
urlpatterns = [
path('list/', CourseListView.as_view(), name='course_list'),
]
现在打开courses/views.py文件,我们添加如下代码:
from django.shortcuts import render
from django.views.generic.base import View
# Create your views here.
class CourseListView(View):
def get(self, request):
return render(request, "course-list.html", {})
运行一下我们的项目,在浏览器地址栏输入:http://127.0.0.1:8000/course/list/然后回车出现:
可以发现访问没有问题,但是样式并没有加载出来。我们仔细观察course_list.html页面之后发现它和之前的org_list.html页面一样是具有共同的头部和尾部,因此我们可以继承base.html页面。
在course_list.html里面删除所有代码,新增以下代码:
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}公开课列表 - 慕海学习网{% endblock %}
{% block custom_bread %}
<section>
<div class="wp">
<ul class="crumbs">
<li><a href="{% url 'index' %}">首页</a>></li>
<li>公开课</li>
</ul>
</div>
</section>
{% endblock %}
{% block content %}
<section> <!--这里面的 <section> 就是课程信息,是通过观测来的-->
{% endblock %}
就是这个样子:
然后是回到我们的courses/views.py文件,我们修改代码如下:
from django.shortcuts import render
from django.views.generic.base import View
# Create your views here.
from .models import Course
class CourseListView(View):
def get(self, request):
all_courses = Course.objects.all()
return render(request, "course-list.html", {
"all_courses": all_courses,
})
回到course_list.html页面,修改代码为图示信息:
分页功能配置
直接仿照我们当时在org_list.html的配置,拷贝如下代码至courses/views.py文件里面:
from pure_pagination import Paginator, EmptyPage, PageNotAnInteger
# 对课程进行分页,尝试获取前端get请求传递过来的page参数
# 如果是不合法的配置参数则默认返回第一页
try:
page = request.GET.get('page', 1)
except PageNotAnInteger:
page = 1
# 这里指从all_courses中取出来,每页显示9个
p = Paginator(all_courses, 9, request=request)
courses = p.page(page)
return render(request, "course-list.html", {
"all_courses": courses,
})
接着打开course_list.html文件,修改代码为图示:
在html中注意此刻不再是all_courses,前面说过此时的all_courses已经不是一个queryset对象,而是一个purepage对象,所以应该用all_courses.object_list。
还有复制org_list的页面分页代码,并将all_orgs修改为all_courses,就是这段代码:
<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="active"><a href="?{{ page.querystring }}">{{ page }}</a></li>
{% else %}
<li><a href="?{{ page.querystring }}">{{ 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>
排序功能的配置
和前面在org_list页面的配置一模一样,我们复制粘贴并修改一下那些代码,放到courses/views.py文件里面:
all_courses = Course.objects.all().order_by("-add_time") # 默认是按添加顺序反着排序
# 最热门和参与人数排名
# 最热门hot根据点击数来判断
# 参与人数是根据学习人数来判断
sort = request.GET.get('sort', '')
if sort:
if sort == "students":
all_courses = all_courses.order_by("-students")
elif sort == "hot":
all_courses = all_courses.order_by("-click_nums")
return render(request, "course-list.html", {
"sort": sort,
})
现在打开course_list.html文件,我们将上述功能和选中状态进行配置:
<ul class="tab_header">
<li class="{% ifequal sort '' %}active{% endifequal %}"><a href="?sort=">最新 </a></li>
<li class="{% ifequal sort 'hot' %}active{% endifequal %}"><a href="?sort=hot">最热门</a></li>
<li class="{% ifequal sort 'students' %}active{% endifequal %}"><a href="?sort=students">参与人数</a></li>
</ul>
就是这个样子:
热门课程推荐
打开courses/views.py文件,新增以下代码:
# 热门课程推荐
hot_courses = Course.objects.all().order_by("-students")[:3]
return render(request, "course-list.html", {
"hot_courses": hot_courses,
})
然后打开course_list.html文件,将热门课程推荐进行动态加载:
运行一下我们的项目,在浏览器地址栏输入http://127.0.0.1:8000/course/list/回车,发现课程难度显示有问题:
我们需要修改{ hot_course.degree }}为 { hot_course.get_degree_display }},这个字段的意思是说得到degree的字段显示,并不是得到它本身,事实上它本身为英文。(专门针对choices的显示)
现在刷新一下我们的页面,难度等级正常显示了,没有问题:至此课程列表页的介绍到此结束,下面我们介绍课程详情页面。
课程详情页配置
老规矩,把前端资料里面的course-detail.html页面拷贝到我们的templates文件夹里面,正常情况下我们应该配置url,但是鉴于此处的course-detail.html页面会继承我们之前的course-list.html页面,所以我们就先把course-detail.html页面给配置完,然后才开始url的配置。
打开course-detail.html页面,删除所有代码,新增以下代码:
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}课程详情页 - 慕海学习网{% endblock %}
{% block custom_bread %}
<section>
<div class="wp">
<ul class="crumbs">
<li><a href="{% url 'index' %}">首页</a>></li>
<li><a href="{% url 'course:course_list' %}">公开课程</a>></li>
<li>{{ course.name }}</li>
</ul>
</div>
</section>
{% endblock %}
{% block content %}
<section>
<section> <!--这里面的 <section> 就是课程信息,是通过观测来的-->
{% endblock %}
接着配置url,打开courses/urls.py文件并在里面新增如下代码:
from .views import CourseDetailView
# 课程机构首页url
re_path('detail/(?P<course_id>.*)/', CourseDetailView.as_view(), name="course_detail"),
现在书写我们的视图函数,打开courses/views.py文件,在里面添加如下代码:
# 课程详情页
class CourseDetailView(View):
def get(self, request,course_id): # 所有re_path在请求参数的时候必须带上id
return render(request, "course-detail.html", {})
然后配置页面的跳转链接,我们希望可以从课程列表页面跳转进来,所以打开course_list.html页面,将图中信息修改为所示信息:
接下来修改我们的view(courses/views.py文件),准备页面信息的动态加载:
# 课程详情页
class CourseDetailView(View):
def get(self, request, course_id):
#注意一下此处的id是数据库表默认为我们添加的
course = Course.objects.get(id=int(course_id))
return render(request, "course-detail.html", {
"course": course,
})
然后打开course_detail.html页面,使我们的数据得到动态加载并显示出来。
注意章节数和学习用户的获取方式:在courses/modles.py文件里面的Course类里面定义一个get-zj-nums方法:
def get_zj_nums(self):
# 获取课程章节数
return self.lesson_set.all().count()
还有学习用户的获取方式:我们之前在operation中专门定义了一个类UserCourse用于对用户学习作记录的。既然存在了这个类(数据库生成和迁移操作之后是一张表),那我们就可以采用获取学习章节数的方式来获取它。
同样在courses/modles.py文件里面的Course类里面定义一个get-learn-users方法:
def get_learn_users(self):
# 获取学习用户数,此处不用统计,我们只取出5个即可
return self.usercourse_set.all()[:5]
就是这个样子:
现在打开course_detail.html页面,将信息修改为图示那样:
然后去xadmin后台新增用户课程,记得把刚才展示的课程加里面去,便于我们观察是否成功显示:
还有一个问题,就是你现在点进这个页面,那么相应课程的点击数应该加1,所以在我们的view(courses/views.py文件),新增以下代码:
# 课程点击数增加
course.click_nums += 1
course.save()
就是这个样子:
授课机构的配置
注意教师数的获取方式:
注意章节数和学习用户的获取方式:在organization/modles.py文件里面的CourseOrg类里面定义一个get_teacher_nums方法:
def get_teacher_nums(self):
# 获取教师数
return self.teacher_set.all().count()
然后html页面调用,采用如下代码:
{{ course.course_org.get_teacher_nums }}
当然如果你不想自定义函数,那也是可以的,你仅仅只需要在前端页面采用如下代码即可,是不是更简单:
{{ course.course_org.teacher_set.count }}
接下来我们完成右侧下面的相关课程推荐模块。
相关课程推荐
这个就是相关课程推荐的页面,我们接下来就是完成这个功能:打开courses/views.py文件,在CourseDetailView函数里面新增用于实现相关课程推荐功能的代码:
# 相关课程推荐
# 此处为course而不是Course,我们是用前面取出的课程
tag = course.tag
if tag:
# 这里必须从1开始不然会推荐自己,也就是索引0
relate_courses = Course.objects.filter(tag=tag)[1:2]
else:
relate_courses = []
return render(request, "course-detail.html", {
"relate_courses": relate_courses,
})
就是这个样子:
接着在course-detail.html页面修改页面展示信息:
{% 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>
{% endfor %}
就是这个样子:
然后去xadmin后台或者数据库中将几个课程的tag修改为同一个,然后再运行一下我们的项目,就出现:
现在还差页面中的收藏和开始学习这两个功能的配置了,开始学习我们在下一篇介绍。
实现收藏功能
我们之前在课程机构里面配置过收藏的功能,如果不熟悉的小伙伴们可以回顾一下第十八篇:
零基础使用Django2.0.1打造在线教育网站(十八):机构详情页配置,这里我就快速介绍一下:
首先在course-detail.html页面底部添加如下代码:
{% block custom_js %}
<script type="text/javascript">
//收藏分享
function add_fav(current_elem, fav_id, fav_type) {
$.ajax({
cache: false,
type: "POST",
url: "{% url "org:add_fav" %}",
data: {'fav_id': fav_id, 'fav_type': fav_type},
async: true,
beforeSend: function (xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
},
success: function (data) {
if (data.status == 'fail') {
if (data.msg == '用户未登录') {
window.location.href = "{% url 'login' %}?next={{ request.path }}";
} else {
alert(data.msg)
}
} else if (data.status == 'success') {
current_elem.text(data.msg)
}
},
});
}
$('#jsLeftBtn').on('click', function () {
add_fav($(this), {{ course.id }}, 1);
});
$('#jsRightBtn').on('click', function () {
add_fav($(this), {{ course.course_org.id }}, 2);
});
</script>
{% endblock %}
然后再打开course/views.py文件,补充 CourseDetailView函数:
from operation.models import UserFavorite
# 是否收藏课程,默认为否
has_fav_course = False
has_fav_org = False
# 用户必须已登录我们才判断,否则不需要
if request.user.is_authenticated:
if UserFavorite.objects.filter(user=request.user, fav_id=course.id, fav_type=1):
has_fav_course = True
if UserFavorite.objects.filter(user=request.user, fav_id=course.course_org.id, fav_type=2):
has_fav_org = True
return render(request, "course-detail.html", {
"has_fav_course": has_fav_course,
"has_fav_org": has_fav_org,
})
就是这个样子:
接着打开course-detail.html页面,修改收藏的显示:
<div class="btn colectgroupbtn" id="jsLeftBtn">
{% if has_fav_course %}已收藏{% else %}收藏{% endif %}
</div>
<div class="btn notlogin
"data-favid="14" id="jsRightBtn">
{% if has_fav_org %}已收藏{% else %}收藏{% endif %}
</div>
运行一下项目,两个都点击试试:
数据库已经有了,点击就没有了。
备注
- 如果出现上面两个收藏按钮点击没反应的情况,大家可以先尝试用浏览器f12(博主用的是chrome浏览器)看一下浏览器有没有post请求发出去以及参数和url的配置是否出错。如果还是有问题,那么回到base.html页面,将
{% block custom_js %}{% endblock %}
的位置由开头放置到底部:
原来:
现在:
知道为什么会出现这种情况么?那是因为jQuery 入口函数与 JavaScript 入口函数是有区别的:jQuery 的入口函数是在 html 所有标签(DOM)都加载之后,就会去执行。而JavaScript 的 window.onload 事件是等到所有内容,包括外部图片之类的文件加载完后,才会执行的。这里是js还未加载完就去执行,所以才出现的问题,放在底部就没问题了。
至此本篇关于课程详情页面的配置介绍就到此结束了,感谢你的赏阅。
本篇笔记对应于第十九篇代码,对应于github的位置是https://github.com/licheetools/eduline。