美文网首页
Django 开发 MxOnline 项目笔记 -- 第8章 课

Django 开发 MxOnline 项目笔记 -- 第8章 课

作者: 江湖十年 | 来源:发表于2018-03-06 00:00 被阅读556次

一、公开课列表页

  • 将课程相关页面拷贝到项目 templates/ 目录下
001.png
  • 课程列表页最终实现效果图
002.png
  1. 回顾 课程 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

  1. 定义 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)

  1. 在 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>

如果直接写成 {{ hot_course.degree }}, 前端页面显示结果

004.png

如果写成 {{ hot_course.get_degree_display }}, 前端页面显示结果

005.png
  1. 配置课程页面 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"),
]

  1. 访问 http://127.0.0.1:8000/course/list/ 查看效果
003.png

二、公开课详情页面

  • 实现效果
006.png
  1. 定义 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)

  1. 配置 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"),
]

  1. 在 course-detail.html 中接收并处理 CourseListView 返回到前端的数据
  • (1)处理并显示课程相关数据
007.png
# 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">时&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;长:</span><span>{{ course.learn_times }}</span></li>
            <li><span class="pram word3">章&nbsp;节&nbsp;数:</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)简单处理课程详情,后期课程详情部分是可以在后台利用富文本编辑的
008.png
# 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)处理授课机构部分
009.png
# 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>课 &nbsp;程&nbsp; 数:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   {{ course.course_org.course_nums }}</span>
            </li>
            <li>
                <span>教 &nbsp;师&nbsp; 数:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {{ course.course_org.teacher_set.count }}</span>
            </li>
            <li>所在地区:&nbsp;&nbsp;{{ course.course_org.address }}</li>
            <li>认&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;证&nbsp;: &nbsp;&nbsp;
                <img title="金牌机构" src="{% static "images/gold.png" %}"/>
            </li>
        </ul>
    </div>
</div>

  • (4)处理相关课程推荐
010.png
# 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)处理课程和课程机构的收藏功能
011.png
  • 参考 第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>

三、课程章节信息页面

  • 用户在公开课详情页面点击"开始学习"按钮后,是要跳转到课程章节信息页面
012.png 013.png
  • 观察页面发现实际上是有两个页面,一个是章节的信息,另一个是评论的信息,两个页面分别是"course-video.html"和"course-comment.html",将两个页面复制到 template 目录下
014.png
  1. 首先完成 "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)课程章节部分功能实现
015.png
# 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)资料下载
016.png
# 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>&nbsp;&nbsp;{{ 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)课程基本信息展示
017.png
# 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)讲师提示
018.png
# 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>

  1. 课程评论页面 "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>

  1. 该课的同学还学过


    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>

  1. 课程播放页面
    在章节信息页面,点击视频标题,新打开一个窗口,课程播放页面,页面除了上部播放视频的窗口,其他地方和章节信息页面展示的信息是相同的


    022.png
023.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 %}

相关文章

网友评论

      本文标题:Django 开发 MxOnline 项目笔记 -- 第8章 课

      本文链接:https://www.haomeiwen.com/subject/kgnfzxtx.html