1. 程式人生 > >Django+Xadmin打造在線教育系統(五)

Django+Xadmin打造在線教育系統(五)

cdn har -o rfi include elf src pytho block

課程相關功能實現

課程列表

創建課程相關的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打造在線教育系統(五)