美文网首页
Django+Xadmin打造在线教育系统(五)

Django+Xadmin打造在线教育系统(五)

作者: 听你讲故事啊 | 来源:发表于2018-09-17 20:38 被阅读0次

    课程相关功能实现

    课程列表

    创建课程相关的urls.py

    path("course/", include('course.urls', namespace="course")),
    


    course里面新建urls.py

    from django.urls import path
    
    from course.views import CourseListView
    
    app_name = "courses"
    urlpatterns = [
        # 课程列表url
        path('list/', CourseListView.as_view(), name="list"),
    
    ]
    

    视图函数

    from django.shortcuts import render
    from django.views.generic import View
    
    class CourseListView(View):
        def get(self, request):
            return render(request, "course-list.html")
    

    course-list.html继承base.html
    修改title,修改bread里面,content里面放course-list独有的
    去后台添加一些课程
    修改视图函数

    class CourseListView(View):
        def get(self, request):
            all_course = Course.objects.all()
            return render(request, "course-list.html", {
                "all_course": all_course,
    
            })
    

    course-list.html文件

        {% for course in all_course %}
            <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 }}&nbsp;&nbsp;</span>
                </div>
                <div class="bottom">
                    <a href="course-detail.html"><span class="fl">来自{{ course.course_org.name }}</span></a>
                    <span class="star fr  notlogin
                        " data-favid="15">
                        {{ course.fav_nums }}
                    </span>
                </div>
            </div>
        {% endfor %}
    

    分页功能

    class CourseListView(View):
        def get(self, request):
            all_course = Course.objects.all()
            # 对课程进行分页
            # 尝试获取前台get请求传递过来的page参数
            # 如果是不合法的配置参数默认返回第一页
            try:
                page = request.GET.get('page', 1)
            except PageNotAnInteger:
                page = 1
            # 这里指从allorg中取五个出来,每页显示5个
            p = Paginator(all_course,6 , request=request)
            courses = p.page(page)
            return render(request, "course-list.html", {
                "all_course":courses,
    
            })
    

    在html中使用时注意object_list,此时的all_course已经不是一个queryset,而是一个purepage对象
    页码的替换参考之前的分页,拿来用即可

    排序功能

    class CourseListView(View):
        def get(self, request):
            all_course = Course.objects.all()
    
            # 进行排序
            sort = request.GET.get('sort', "")
            if sort:
                if sort == "students":
                    all_course = all_course.order_by("-students")
                elif sort == "hot":
                    all_course = all_course.order_by("-click_nums")
    
            # 热门课程推荐
            hot_courses = Course.objects.all().order_by("-students")[:3]
    
    
            # 对课程进行分页
            # 尝试获取前台get请求传递过来的page参数
            # 如果是不合法的配置参数默认返回第一页
            try:
                page = request.GET.get('page', 1)
            except PageNotAnInteger:
                page = 1
            # 这里指从allorg中取五个出来,每页显示5个
            p = Paginator(all_course,6 , request=request)
            courses = p.page(page)
            return render(request, "course-list.html", {
                "all_course":courses,
                "sort": sort,
                "hot_courses": hot_courses
            })
    
        <div class="head">
            <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>
        </div>
    
        <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 %}
    

    课程详情

    模板替换,
    路由配置

    # 课程详情页
    re_path('course/(?P<course_id>\d+)/', CourseDetailView.as_view(), name="course_detail"),
    
    class CourseDetailView(View):
        '''课程详情'''
        def get(self, request, course_id):
            course = Course.objects.get(id=int(course_id))
            # 课程的点击数加1
            course.click_nums += 1
            course.save()
            return  render(request, "course-detail.html", {
                'course':course,
    
            })
    

    课程的章节数如何实现?

    models.py中自定义方法

        def get_zj_nums(self):
            # 获取课程章节数的方法
            return self.lesson_set.all().count()
    
        # 获取学习这门课程的用户
        def get_learn_users(self):
            # 谁的里面添加了它做外键,他都可以取出来
            return self.usercourse_set.all()[:5]
    

    添加课程类别字段

        category = models.CharField(max_length=20, default="", verbose_name="课程类别")
    

    修改course-detail.html内容

        <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.desc }}</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.get_zj_nums }}</span></li>
                <li><span class="pram word3">课程类别:</span><span title="">{{ course.category }}</span></li>
                <li class="piclist"><span class="pram word4">学习用户:</span>
                    {% for user_course in course.get_learn_users %}
                        <span class="pic"><img width="40" height="40" src="{{ MEDIA_URL }}{{ user_course.user.image }}"/></span>
                    {% endfor %}
                </li>
            </ul>
        <div class="btns">
    
        <div class="head">
            <ul class="tab_header">
                <li class="active">课程详情</li>
            </ul>
        </div>
        <div class="tab_cont tab_cont1">
            {{ course.detail }}
        </div>
    

    授课机构

    CourseOrg model添加一个获取教师数的方法

        def get_teacher_nums(self):
            #获取机构的教师数
            return self.teacher_set.all().count()
    

    授课机构显示

        <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 }}</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.get_teacher_nums }}</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>
    

    相关课程推荐

    定义课程的tag ,如果tag相同,那么是相关课程。

    courses/models.py:

    tag = models.CharField(max_length=15, verbose_name="课程标签", default="")
    
    class CourseDetailView(View):
        '''课程详情'''
        def get(self, request, course_id):
            course = Course.objects.get(id=int(course_id))
            # 课程的点击数加1
            course.click_nums += 1
            course.save()
            # 课程标签
            # 通过当前标签,查找数据库中的课程
            tag = course.tag
            if tag:
                # 需要从1开始不然会推荐自己
                relate_courses = Course.objects.filter(tag=tag)[:3]
            else:
                relate_courses = []
            return  render(request, "course-detail.html", {
                'course':course,
                'relate_courses':relate_courses,
            })
    

    前端

        <div class="right layout">
            <div class="head">相关课程推荐</div>
            <div class="group_recommend">
                {% for relate_course in relate_courses %}
                <dl>
                    <dt>
                        <a target="_blank" href="">
                            <img width="240" height="220" class="scrollLoading" src="{{ MEDIA_URL }}{{ relate_course.image }}"/>
                        </a>
                    </dt>
                    <dd>
                        <a target="_blank" href=""><h2> {{ relate_course.name }}</h2></a>
                        <span class="fl">学习时长:<i class="key">{{ relate_course.learn_times }}</i></span>
                    </dd>
                </dl>
                {% endfor %}
            </div>
        </div>
    

    收藏功能

    ajax

        <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="/login/";
                        }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>
    
    class CourseDetailView(View):
        '''课程详情'''
        def get(self, request, course_id):
            course = Course.objects.get(id=int(course_id))
            # 课程的点击数加1
            course.click_nums += 1
            course.save()
            # 课程标签
            # 通过当前标签,查找数据库中的课程
            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
            tag = course.tag
            if tag:
                # 需要从1开始不然会推荐自己
                relate_courses = Course.objects.filter(tag=tag)[:2]
            else:
                relate_courses = []
            return  render(request, "course-detail.html", {
                'course':course,
                'relate_courses':relate_courses,
                "has_fav_course": has_fav_course,
                "has_fav_org": has_fav_org,
            })
    

    修改前端代码

        {% if has_fav_org %}
            已收藏
        {% else %}
            收藏
        {% endif %}
    

    课程章节信息

    章节信息,评论信息
    模板替换

    # 课程章节信息页
        re_path('info/(?P<course_id>\d+)/', CourseInfoView.as_view(), name="course_info"),
    
    class CourseInfoView(View):
        '''课程章节信息'''
        def get(self, request, course_id):
            course = Course.objects.get(id=int(course_id))
            return render(request, "course-video.html", {
                "course": course,
            })
    

    修改开始学习链接

        <div class="buy btn"><a style="color: white" href="{% url 'course:course_info' course.id %}">开始学习</a></div>
    

    章节视频信息

    为video表添加视频对应的url信息

    url = models.CharField(max_length=200, default="" ,verbose_name="访问地址")
    

    给Vedio添加一个学习时长的字段

    # 使用分钟做后台记录(存储最小单位)前台转换
       learn_times = models.IntegerField(default=0, verbose_name="学习时长(分钟数)")
    

    给Course添加一个获取章节的方法

        def get_course_lesson(self):
            #获取课程的章节
            return self.lesson_set.all()
    

    给Lesson添加一个获取所有视频的方法

        def get_lesson_vedio(self):
            #获取章节所有视频
            return self.video_set.all()
    

    后台添加视频信息
    前端页面展示

        <div class="mod-chapters">
            {% for lesson in course.get_course_lesson %}
            <div class="chapter chapter-active" >
                <h3>
                    <strong><i class="state-expand"></i>{{ lesson.name }}</strong>
                </h3>
                <ul class="video">
                    {% for vedio in lesson.get_lesson_vedio %}
                    <li>
                        <a target="_blank" href='{{ vedio.url }}' class="J-media-item studyvideo">{{ vedio.name }} ({{ vedio.learn_times }})
                            <i class="study-state"></i>
                        </a>
                    </li>
                    {% endfor %}
                </ul>
            </div>
            {% endfor %}
        </div>
    

    资料下载

    后台自行上传点文件

    class CourseInfoView(View):
        def get(self,request,course_id):
            course = Course.objects.get(id=int(course_id))
            all_resources = CourseResource.objects.filter(course=course)
            return render(request,'course-video.html',{'course':course,'all_resources':all_resources})
    
        <div class="box mb40">
            <h4>资料下载</h4>
            <ul class="downlist">
                {% for course_resource in all_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>
    

    讲师提示

    创建课程与讲师之间的关联,给Course添加一个Teacher外键

    # Course
    teacher = models.ForeignKey(Teacher,verbose_name='讲师',null=True,blank=True,on_delete=models.CASCADE)
    

    给Course再添加两个字段 “课程须知”和“老师告诉你能学到什么”

        you_need_know = models.CharField(max_length=300, default="一颗勤学的心是本课程必要前提", verbose_name="课程须知")
        teacher_tell = models.CharField(max_length=300, default="按时交作业,不然叫家长", verbose_name="老师告诉你")
    

    修改前端显示

        <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.you_need_know }}</dd>
                </dl>
                <dl>
                    <dt>老师告诉你能学到什么?</dt>
                    <dd class="autowrap">{{ course.teacher_tell }}</dd>
                </dl>
            </div>
        </div>
    

    课程评论页面

    修改模板
    配置url

    #课程评论
        re_path('comment/(?P<course_id>\d+)/', CommentsView.as_view(), name="course_comments"),
    
    class CommentsView(View):
        def get(self, request, course_id):
            # 此处的id为表默认为我们添加的值。
            course = Course.objects.get(id=int(course_id))
            all_resources = CourseResource.objects.filter(course=course)
            return render(request, "course-comment.html", {
                "course": course,
                "all_resources": all_resources,
            })    
    

    在course-vedio.html中修改 “章节”和“评论”的链接

        <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_comments' course.id %}"><span>评论</span></a></li>
            </ul>
        </div>
    

    发表评论功能
    Ajax操作。如果发布成功就会刷新页面

    # ajax方式添加评论
    class AddCommentsView(View):
        def post(self, request):
            if not request.user.is_authenticated:
                # 未登录时返回json提示未登录,跳转到登录页面是在ajax中做的
                return HttpResponse('{"status":"fail", "msg":"用户未登录"}', content_type='application/json')
            course_id = request.POST.get("course_id", 0)
            comments = request.POST.get("comments", "")
            if int(course_id) > 0 and comments:
                course_comments = CourseComments()
                # get只能取出一条数据,如果有多条抛出异常。没有数据也抛异常
                # filter取一个列表出来,queryset。没有数据返回空的queryset不会抛异常
                course = Course.objects.get(id = int(course_id))
                # 外键存入要存入对象
                course_comments.course = course
                course_comments.comments = comments
                course_comments.user = request.user
                course_comments.save()
                return HttpResponse('{"status":"success", "msg":"评论成功"}', content_type='application/json')
            else:
                return HttpResponse('{"status":"fail", "msg":"评论失败"}', content_type='application/json')
    
    # 添加课程评论,已经把参数放到post当中了
    path('add_comment/', AddCommentsView.as_view(), name="add_comment"),
    

    Ajax

        <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) {
                        if(data.status == 'fail'){
                            if(data.msg == '用户未登录'){
                                window.location.href="/login/";
                            }else{
                                alert(data.msg)
                            }
    
                        }else if(data.status == 'success'){
                            window.location.reload();//刷新当前页面.
                        }
                    },
                });
            });
    
        </script>
    

    相关课程推荐

    CourseInfoView添加

            # 选出学了这门课的学生关系
            user_courses = UserCourse.objects.filter(course= course)
            # 从关系中取出user_id
            user_ids = [user_course.user_id for user_course in user_courses]
            # 这些用户学了的课程,外键会自动有id,取到字段
            all_user_courses = UserCourse.objects.filter(user_id__in=user_ids)
            # 取出所有课程id
            course_ids = [all_user_course.course_id for all_user_course in all_user_courses]
            # 获取学过该课程用户学过的其他课程
            relate_courses = Course.objects.filter(id__in=course_ids).order_by("-click_nums")[:5]
    
            return render(request, "course-video.html", {
                "course": course,
                "all_resources": all_resources,
                "relate_courses":relate_courses,
            })
    

    修改前端代码

        <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>
    

    把课程与用户关联起来

    当用户点了“开始学习”之后,应该把这门课程与用户关联起来,在这之前应该需要做个判断,如果没有登录,则让用户先登录才可以。
    如果是用函数方式写的话直接加个装饰器(@login_required)就可以,但是我们是用类的方式写的,必须用继承的方式

    在utils目录下创建文件 mixin_utils.py(最基本的类都放在mixin_utils.py里面),代码如下:

    from django.contrib.auth.decorators import login_required
    from django.utils.decorators import method_decorator
    
    class LoginRequiredMixin(object):
        @method_decorator(login_required(login_url='/login/'))   
        def dispatch(self,request,*args,**kwargs):
            return super(LoginRequiredMixin, self).dispatch(request,*args,**kwargs)
    

    然后让CourseInfoViewCommentsView都继承LoginRequiredMixin
    继承后,没有登录的用户点“开始学习”,自动跳到login界面
    把用户与课程关联起来

     # CourseInfoView
            # 查询用户是否已经学习了该课程
            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()
    

    视频播放页面

    把course-paly.html拷贝到templates目录下
    使用开源库video js,这里使用CDN

    <link href="https://vjs.zencdn.net/7.1.0/video-js.css" rel="stylesheet">
    <script src="https://vjs.zencdn.net/7.1.0/video.js"></script>
    
    # 课程视频播放页
        path('video/(?P<video_id>\d+)/', VideoPlayView.as_view(), name="video_play"),
    
        <li>
            <a target="_blank" href='{% url 'course:video_play' vedio.id %}' class="J-media-item studyvideo">{{ vedio.name }} ({{ vedio.learn_times }})
                <i class="study-state"></i>
            </a>
        </li>
    
    class VideoPlayView(LoginRequiredMixin, View):
        '''课程章节视频播放页面'''
        def get(self,request,video_id):
            video = Video.objects.get(id=int(video_id))
            #通过外键找到章节再找到视频对应的课程
            course = video.lesson.course
    
            course.students += 1
            course.save()
    
            # 查询用户是否已经学习了该课程
            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()
    
            #相关课程推荐
            # 找到学习这门课的所有用户
            user_courses = UserCourse.objects.filter(course=course)
            # 找到学习这门课的所有用户的id
            user_ids = [user_course.user_id for user_course in user_courses]
            # 通过所有用户的id,找到所有用户学习过的所有过程
            all_user_courses = UserCourse.objects.filter(user_id__in=user_ids)
            # 取出所有课程id
            course_ids = [all_user_course.course_id for all_user_course in all_user_courses]
            # 通过所有课程的id,找到所有的课程,按点击量去五个
            relate_courses = Course.objects.filter(id__in=course_ids).order_by("-click_nums")[:5]
    
            # 资源
            all_resources = CourseResource.objects.filter(course=course)
            return render(request,'course-play.html',{
                'course':course,
                'all_resources':all_resources,
                'relate_courses':relate_courses,
                'video':video,
            })
    

    相关文章

      网友评论

          本文标题:Django+Xadmin打造在线教育系统(五)

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