(项目)在线教育平台(十一)

发布时间:2019-04-09 21:44:47编辑:auto阅读(2313)

    十五、首页全局配置

    1、首页前端页面配置

      将index.html继承base.html页面,修改继承的block地方:

      修改base.html页面中导航栏选中状态的代码:

    2、全局搜索功能

      首页的全局搜索功能可以对课程,机构,教师进行全局搜索,搜索的代码放在deco-common.js文件中:

     1 //顶部搜索栏搜索方法
     2 function search_click(){
     3     var type = $('#jsSelectOption').attr('data-value'),
     4         keywords = $('#search_keywords').val(),
     5         request_url = '';
     6     if(keywords == ""){
     7         return
     8     }
     9     if(type == "course"){
    10         request_url = "/course/list?keywords="+keywords
    11     }else if(type == "teacher"){
    12         request_url = "/org/teacher/list?keywords="+keywords
    13     }else if(type == "org"){
    14         request_url = "/org/list?keywords="+keywords
    15     }
    16     window.location.href = request_url
    17 }

      只需要在课程列表接口、机构列表接口、讲师列表接口中加入搜索的逻辑即可:

     1 class CourseListView(View):
     2     """课程列表页"""
     3     def get(self, request):
     4         # 获取所有的课程
     5         all_courses = Course.objects.all()
     6 
     7         # 排序(学习人数,点击数)
     8         sort = request.GET.get('sort', '')
     9         if sort:
    10             if sort == 'students':
    11                 all_courses = all_courses.order_by('-students')
    12             elif sort == 'hot':
    13                 all_courses = all_courses.order_by('-click_nums')
    14 
    15         # 热门课程
    16         hot_courses = all_courses.order_by('-click_nums')[:2]
    17 
    18         # 搜索
    19         search_keywords = request.GET.get('keywords', '')
    20         if search_keywords:
    21             all_courses = all_courses.filter(Q(name__icontains=search_keywords)|
    22                                              Q(desc__icontains=search_keywords)|
    23                                              Q(detail__icontains=search_keywords))
    24 
    25         # 分页
    26         try:
    27             page = request.GET.get('page', 1)
    28         except PageNotAnInteger:
    29             page = 1
    30         p = Paginator(all_courses, 3, request=request)
    31         courses = p.page(page)
    32 
    33         return render(request, 'course-list.html', {
    34             'all_courses': courses,
    35             'sort': sort,
    36             'hot_courses': hot_courses
    37         })
    课程列表接口
     1 class OrgView(View):
     2     """机构列表"""
     3     def get(self, request):
     4         # 取出所有的机构
     5         all_orgs = CourseOrg.objects.all()
     6 
     7         # 取出所有的城市
     8         all_citys = CityDict.objects.all()
     9 
    10         # 搜索
    11         search_keywords = request.GET.get('keywords', '')
    12         if search_keywords:
    13             all_orgs = all_orgs.filter(Q(name__icontains=search_keywords)|
    14                                        Q(desc__icontains=search_keywords))
    15 
    16         # 排名筛选(根据点击量排名)
    17         hot_orgs = all_orgs.order_by('-click_nums')[:3]
    18 
    19         # 学习人数和课程数排名筛选
    20         sort = request.GET.get('sort', '')
    21         if sort:
    22             if sort == 'students':
    23                 all_orgs = all_orgs.order_by('-students')
    24             elif sort == 'courses':
    25                 all_orgs = all_orgs.order_by('-course_nums')
    26 
    27         # 城市筛选(从request中获取城市的id)
    28         city_id = request.GET.get('city', '')
    29         if city_id:
    30             all_orgs = all_orgs.filter(city_id=int(city_id))
    31 
    32         # 类别筛选(从request中获取机构类别ct)
    33         category = request.GET.get('ct', '')
    34         if category:
    35             all_orgs = all_orgs.filter(category=category)
    36 
    37         # 筛选完再统计数量
    38         org_nums = all_orgs.count()
    39 
    40         # 分页
    41         try:
    42             page = request.GET.get('page', 1)
    43         except PageNotAnInteger:
    44             page = 1
    45         p = Paginator(all_orgs, 5, request=request)
    46         orgs = p.page(page)
    47 
    48         return render(request, 'org-list.html', {
    49             'all_orgs': orgs,
    50             'all_citys': all_citys,
    51             'org_nums': org_nums,
    52             'city_id': city_id,
    53             'category': category,
    54             'hot_orgs': hot_orgs,
    55             'sort': sort
    56         })
    机构列表接口
     1 class TeacherListView(View):
     2     """讲师列表页面"""
     3     def get(self, request):
     4         # 获取所有的讲师
     5         all_teachers = Teacher.objects.all()
     6 
     7         # 搜索
     8         search_keywords = request.GET.get('keywords', '')
     9         if search_keywords:
    10             all_teachers = all_teachers.filter(name__icontains=search_keywords)
    11 
    12         # 统计讲师的总数
    13         teacher_nums = all_teachers.count()
    14 
    15         # 排序,按点击数排序
    16         sort = request.GET.get('sort', '')
    17         if sort:
    18             if sort == 'hot':
    19                 all_teachers = all_teachers.order_by('-click_nums')
    20 
    21         # 讲师排行版
    22         teacher_sorted = all_teachers.order_by('-click_nums')[:3]
    23 
    24         # 分页
    25         try:
    26             page = request.GET.get('page', 1)
    27         except PageNotAnInteger:
    28             page = 1
    29         p = Paginator(all_teachers, 5, request=request)
    30         teachers = p.page(page)
    31 
    32         return render(request, 'teachers-list.html', {
    33             'all_teachers': teachers,
    34             'teacher_nums': teacher_nums,
    35             'sort': sort,
    36             'teacher_sorted': teacher_sorted
    37         })
    讲师列表接口

    十六、个人中心

    1、个人中心页面

    1.1 个人中心前端页面配置

      在templates目录下新建usercenter-base.html文件,作为个人中心页面的母版,然后将usercenter-info.html页面拷贝到该目录下,并把其中的内容拷贝到母版中,修改其中需要block的地方:

      1 {% load staticfiles %}
      2 
      3 <!DOCTYPE html>
      4 <html>
      5 
      6 <head>
      7     <meta charset="UTF-8">
      8     <meta name="renderer" content="webkit">
      9     <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" >
     10     <title>{% block title %}个人信息- 知能网{% endblock %}</title>
     11     <link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}">
     12     <link rel="stylesheet" type="text/css" href="{% static 'css/animate.css' %}">
     13     <link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
     14     <link rel="stylesheet" type="text/css" href="{% static 'js/plugins/queryCity/css/cityLayout.css' %}">
     15 
     16     <link rel="stylesheet" type="text/css" href="{% static 'css/lq.datetimepick.css' %}"/>
     17     {% block custom_css %}{% endblock %}
     18 
     19 
     20     <script src="{% static 'js/jquery.min.js' %}" type="text/javascript"></script>
     21     <script src="{% static 'js/jquery-migrate-1.2.1.min.js' %}" type="text/javascript"></script>
     22 
     23 </head>
     24 <body>
     25 <section class="headerwrap headerwrap2">
     26     <header>
     27         <div  class="header2 header">
     28              <div class="top">
     29                 <div class="wp">
     30                     <div class="fl"><p>服务电话:<b>13993601652</b></p></div>
     31                     <!--登录后跳转-->
     32 
     33 
     34                         <div class="personal">
     35                             <dl class="user fr">
     36                                 <dd>bobby<img class="down fr" src="{% static 'images/top_down.png' %}"/></dd>
     37                                 <dt><img width="20" height="20" src="{% static 'media/image/2016/12/default_big_14.png' %}"/></dt>
     38                             </dl>
     39                             <div class="userdetail">
     40                                 <dl>
     41                                     <dt><img width="80" height="80" src="{% static 'media/image/2016/12/default_big_14.png' %}"/></dt>
     42                                     <dd>
     43                                         <h2>django</h2>
     44                                         <p>bobby</p>
     45                                     </dd>
     46                                 </dl>
     47                                 <div class="btn">
     48                                     <a class="personcenter fl" href="usercenter-info.html">进入个人中心</a>
     49                                     <a class="fr" href="/logout/">退出</a>
     50                                 </div>
     51                             </div>
     52                         </div>
     53                         <a href="usercenter-message.html">
     54                             <div class="msg-num"><span id="MsgNum">0</span></div>
     55                         </a>
     56 
     57 
     58                 </div>
     59             </div>
     60 
     61     <div class="middle">
     62         <div class="wp">
     63             <a href="index.html"><img class="fl" src="{% static 'images/logo2.png' %}"/></a>
     64             <h1>我的知能网</h1>
     65         </div>
     66     </div>
     67             </div>
     68     </header>
     69 </section>
     70 
     71 
     72 
     73 <!--crumbs start-->
     74 {% block custom_bread %}
     75 
     76 {% endblock %}
     77 
     78 
     79 <section>
     80     <div class="wp list personal_list">
     81     <div class="left">
     82         <ul>
     83             <li class="active2"><a href="usercenter-info.html">个人资料</a></li>
     84             <li ><a href="usercenter-mycourse.html">我的课程</a></li>
     85             <li ><a href="usercenter-fav-course.html">我的收藏</a></li>
     86             <li >
     87                 <a href="usercenter-message.html" style="position: relative;">
     88                     我的消息
     89                 </a>
     90             </li>
     91         </ul>
     92     </div>
     93 
     94 
     95     {% block custom_right_content %}
     96     
     97     {% endblock %}
     98 
     99 
    100     </div>
    101 </section>
    102 
    103 <!--sidebar start-->
    104 <section>
    105     <ul class="sidebar">
    106         <li class="qq">
    107             <a target="_blank" href="http://wpa.qq.com/msgrd?v=3&uin=2023525077&site=qq&menu=yes"></a>
    108         </li>
    109         <li class="totop"></li>
    110     </ul>
    111 </section>
    112 <!--sidebar end-->
    113 <!--header start-->
    114 
    115 <div class="dialog" id="jsDialog">
    116     <div class="successbox dialogbox" id="jsSuccessTips">
    117         <h1>成功提交</h1>
    118         <div class="close jsCloseDialog"><img src="../images/dig_close.png"/></div>
    119         <div class="cont">
    120             <h2>您的需求提交成功!</h2>
    121             <p></p>
    122         </div>
    123     </div>
    124     <!--提示弹出框-->
    125     <div class="bidtips dialogbox promptbox" id="jsComfirmDialig">
    126         <h1>确认提交</h1>
    127         <div class="close jsCloseDialog"><img src="../images/dig_close.png"/></div>
    128         <div class="cont">
    129             <h2>您确认提交吗?</h2>
    130             <dd class="autoTxtCount">
    131                 <div class="button">
    132                     <input type="button" class="fl half-btn" value="确定" id="jsComfirmBtn"/>
    133                     <span class="fr half-btn jsCloseDialog">取消</span>
    134                 </div>
    135             </dd>
    136         </div>
    137     </div>
    138     <div class="resetpwdbox dialogbox" id="jsResetDialog">
    139         <h1>修改密码</h1>
    140         <div class="close jsCloseDialog"><img src="../images/dig_close.png"/></div>
    141         <div class="cont">
    142             <form id="jsResetPwdForm" autocomplete="off">
    143                 <div class="box">
    144                     <span class="word2" >&nbsp;&nbsp;&nbsp;&nbsp;</span>
    145                     <input type="password" id="pwd" name="password1" placeholder="6-20位非中文字符"/>
    146                 </div>
    147                 <div class="box">
    148                     <span class="word2" >确定密码</span>
    149                     <input type="password" id="repwd" name="password2" placeholder="6-20位非中文字符"/>
    150                 </div>
    151                 <div class="error btns" id="jsResetPwdTips"></div>
    152                 <div class="button">
    153                     <input id="jsResetPwdBtn" type="button" value="提交" />
    154                 </div>
    155                 <input type='hidden' name='csrfmiddlewaretoken' value='DaP7IUKm9FA9nELA9YUlYYWpyIDdCiIP' />
    156             <input type='hidden' name='csrfmiddlewaretoken' value='799Y6iPeEDNSGvrTu3noBrO4MBLv6enY' />
    157             </form>
    158         </div>
    159     </div>
    160     <div class="dialogbox changeemai1 changephone" id="jsChangeEmailDialog">
    161         <h1>修改邮箱</h1>
    162         <div class="close jsCloseDialog"><img src="../images/dig_close.png"/></div>
    163         <p>请输入新的邮箱地址</p>
    164         <form id="jsChangeEmailForm" autocomplete="off">
    165             <div class="box">
    166                 <input class="fl change_email" name="email" id="jsChangeEmail" type="text" placeholder="输入重新绑定的邮箱地址">
    167             </div>
    168             <div class="box">
    169                 <input class="fl email_code" type="text" id="jsChangeEmailCode" name="code" placeholder="输入邮箱验证码">
    170                 <input class="getcode getting" type="button" id="jsChangeEmailCodeBtn" value="获取验证码">
    171             </div>
    172             <div class="error btns change_email_tips" id="jsChangeEmailTips" >请输入...</div>
    173             <div class="button">
    174                 <input class="changeemai_btn" id="jsChangeEmailBtn" type="button" value="完成"/>
    175             </div>
    176             <input type='hidden' name='csrfmiddlewaretoken' value='DaP7IUKm9FA9nELA9YUlYYWpyIDdCiIP' />
    177         <input type='hidden' name='csrfmiddlewaretoken' value='799Y6iPeEDNSGvrTu3noBrO4MBLv6enY' />
    178         </form>
    179     </div>
    180 
    181     <div  class="noactivebox dialogbox" id="jsUnactiveForm" >
    182         <h1>邮件验证提示</h1>
    183         <div class="close jsCloseDialog"><img src="../images/dig_close.png"/></div>
    184         <div class="center">
    185             <img src="../images/send.png"/>
    186             <p>我们已经向您的邮箱<span class="green" id="jsEmailToActive">12@13.com</span>发送了邮件,<br/>为保证您的账号安全,请及时验证邮箱</p>
    187             <p class="a"><a class="btn" id="jsGoToEmail" target="_blank" href="http://mail.qq.com">去邮箱验证</a></p>
    188             <p class="zy_success upmove"></p>
    189             <p style="display: none;" class="sendE2">没收到,您可以查看您的垃圾邮件和被过滤邮件,也可以再次发送验证邮件(<span class="c5c">60s</span></p>
    190             <p class="sendE">没收到,您可以查看您的垃圾邮件和被过滤邮件,<br/>也可以<span class="c5c green" id="jsSenEmailAgin" style="cursor: pointer;">再次发送验证邮件</span></p>
    191         </div>
    192     </div>
    193     <div class="resetpassbox dialogbox" id="jsSetNewPwd">
    194         <h1>重新设置密码</h1>
    195         <div class="close jsCloseDialog"><img src="../images/dig_close.png"/></div>
    196         <p class="green">请输入新密码</p>
    197         <form id="jsSetNewPwdForm">
    198             <div class="box">
    199                 <span class="word2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
    200                 <input type="password" name="password" id="jsResetPwd" placeholder="请输入新密码"/>
    201             </div>
    202             <div class="box">
    203                 <span class="word2">&nbsp;&nbsp;&nbsp;</span>
    204                 <input type="password" name="password2" id="jsResetPwd2" placeholder="请再次输入新密码"/>
    205             </div>
    206             <div class="box">
    207                 <span class="word2">&nbsp;&nbsp;&nbsp;&nbsp;</span>
    208                 <input type="text" name="code" id="jsResetCode" placeholder="请输入手机验证码"/>
    209             </div>
    210             <div class="error btns" id="jsSetNewPwdTips"></div>
    211             <div class="button">
    212                 <input type="hidden" name="mobile" id="jsInpResetMobil" />
    213                 <input id="jsSetNewPwdBtn" type="button" value="提交" />
    214             </div>
    215             <input type='hidden' name='csrfmiddlewaretoken' value='DaP7IUKm9FA9nELA9YUlYYWpyIDdCiIP' />
    216         </form>
    217     </div>
    218     <div class="forgetbox dialogbox">
    219         <h1>忘记密码</h1>
    220         <div class="close jsCloseDialog"><img src="../images/dig_close.png"/></div>
    221         <div class="cont">
    222             <form id="jsFindPwdForm" autocomplete="off">
    223                 <input type='hidden' name='csrfmiddlewaretoken' value='DaP7IUKm9FA9nELA9YUlYYWpyIDdCiIP' />
    224                 <div class="box">
    225                     <span class="word2" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
    226                     <input type="text" id="account" name="account" placeholder="手机/邮箱"/>
    227                 </div>
    228                 <div class="box">
    229                     <span class="word3">验证码</span>
    230                     <input autocomplete="off" class="form-control-captcha find-password-captcha" id="find-password-captcha_1" name="captcha_f_1" placeholder="请输入验证码" type="text" /> <input class="form-control-captcha find-password-captcha" id="find-password-captcha_0" name="captcha_f_0" placeholder="请输入验证码" type="hidden" value="5f3c00e47fb1be12d2fd15b9a860711597721b3f" /> &nbsp;<img src="/captcha/image/5f3c00e47fb1be12d2fd15b9a860711597721b3f/" alt="captcha" class="captcha" />
    231                 </div>
    232                 <div class="error btns" id="jsForgetTips"></div><!--忘记密码错误-->
    233                 <div class="button">
    234                     <input type="hidden" name="sms_type" value="1">
    235                     <input id="jsFindPwdBtn" type="button" value="提交" />
    236                 </div>
    237             </form>
    238         </div>
    239     </div>
    240 </div>
    241 <div class="bg" id="dialogBg"></div>
    242 
    243 
    244 <script src="{% static 'js/selectUi.js' %}" type='text/javascript'></script>
    245 <script type="text/javascript" src="{% static 'js/plugins/laydate/laydate.js' %}"></script>
    246 <script src="{% static 'js/plugins/layer/layer.js' %}"></script>
    247 <script src="{% static 'js/plugins/queryCity/js/public.js' %}" type="text/javascript"></script>
    248 <script src="{% static 'js/unslider.js' %}" type="text/javascript"></script>
    249 <script src="{% static 'js/plugins/jquery.scrollLoading.js' %}"  type="text/javascript"></script>
    250 <script src="{% static 'js/validateDialog.js' %}"  type="text/javascript"></script>
    251 <script src="{% static 'js/deco-common.js' %}"  type="text/javascript"></script>
    252 
    253 <script src='{% static 'js/plugins/jquery.upload.js' %}' type='text/javascript'></script>
    254 <script src="{% static 'js/validate.js' %}" type="text/javascript"></script>
    255 <script src="{% static 'js/deco-user.js' %}"></script>
    256 
    257 
    258 {% block custom_js %}
    259 
    260 {% endblock %}
    261 
    262 <script type="text/javascript">
    263     $('.jsDeleteFav_course').on('click', function(){
    264         var _this = $(this),
    265             favid = _this.attr('data-favid');
    266         alert(favid)
    267         $.ajax({
    268                 cache: false,
    269                 type: "POST",
    270                 url: "/org/add_fav/",
    271                 data: {
    272                     fav_type: 1,
    273                     fav_id: favid,
    274                     csrfmiddlewaretoken: '799Y6iPeEDNSGvrTu3noBrO4MBLv6enY'
    275                 },
    276                 async: true,
    277                 success: function(data) {
    278                     Dml.fun.winReload();
    279                 }
    280             });
    281     });
    282 
    283     $('.jsDeleteFav_teacher').on('click', function(){
    284             var _this = $(this),
    285                 favid = _this.attr('data-favid');
    286             $.ajax({
    287                     cache: false,
    288                     type: "POST",
    289                     url: "/org/add_fav/",
    290                     data: {
    291                         fav_type: 3,
    292                         fav_id: favid,
    293                         csrfmiddlewaretoken: '799Y6iPeEDNSGvrTu3noBrO4MBLv6enY'
    294                     },
    295                     async: true,
    296                     success: function(data) {
    297                         Dml.fun.winReload();
    298                     }
    299                 });
    300         });
    301 
    302 
    303     $('.jsDeleteFav_org').on('click', function(){
    304             var _this = $(this),
    305                 favid = _this.attr('data-favid');
    306             $.ajax({
    307                     cache: false,
    308                     type: "POST",
    309                     url: "/org/add_fav/",
    310                     data: {
    311                         fav_type: 2,
    312                         fav_id: favid,
    313                         csrfmiddlewaretoken: '799Y6iPeEDNSGvrTu3noBrO4MBLv6enY'
    314                     },
    315                     async: true,
    316                     success: function(data) {
    317                         Dml.fun.winReload();
    318                     }
    319                 });
    320         });
    321 </script>
    322 
    323 
    324 <script>
    325         var shareUrl = '',
    326             shareText = '',
    327             shareDesc = '',
    328             shareComment = '';
    329         $(function () {
    330             $(".bdsharebuttonbox a").mouseover(function () {
    331                 var type = $(this).attr('data-cmd'),
    332                     $parent = $(this).parent('.bdsharebuttonbox'),
    333                     fxurl = $parent.attr('data-url'),
    334                     fxtext = $parent.attr('data-text'),
    335                     fxdesc = $parent.attr('data-desc'),
    336                     fxcomment = $parent.attr('data-comment');
    337                 switch (type){
    338                     case 'tsina':
    339                     case 'tqq':
    340                     case 'renren':
    341                             shareUrl = fxurl;
    342                             shareText = fxdesc;
    343                             shareDesc = '';
    344                             shareComment = '';
    345                         break;
    346                     default :
    347                             shareUrl = fxurl;
    348                             shareText = fxtext;
    349                             shareDesc = fxdesc;
    350                             shareComment = fxcomment;
    351                         break;
    352                 }
    353             });
    354         });
    355         function SetShareUrl(cmd, config) {
    356             if (shareUrl) {
    357                 config.bdUrl = "" + shareUrl;
    358             }
    359             if(shareText){
    360                 config.bdText = shareText;
    361             }
    362             if(shareDesc){
    363                 config.bdDesc = shareDesc;
    364             }
    365             if(shareComment){
    366                 config.bdComment = shareComment;
    367             }
    368 
    369             return config;
    370         }
    371         window._bd_share_config = {
    372             "common": {
    373                 "onBeforeClick":SetShareUrl,
    374                 "bdPic":"",
    375                 "bdMini":"2",
    376                 "searchPic":"1",
    377                 "bdMiniList":false
    378             },
    379             "share": {
    380                 "bdSize":"16"
    381             }
    382         };
    383     with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com../api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
    384 </script>
    385 </body>
    386 </html>
    usercenter-base.html

      然后将usercenter-info.html页面继承母版:

    1.2 个人信息接口

      在users/views.py中编写个人中心的接口:

    1 class UserInfoView(View):
    2     """个人中心页面"""
    3     def get(self, request):
    4         return render(request, 'usercenter-info.html', {
    5 
    6         })

      先在users下新建urls.py文件,然后在MxOnline/urls.py中配置个人信息的路由分发:

    1 urlpatterns = [
    2     path('users/', include('users.urls', namespace='users')),  # 个人信息
    3 ]

      在users/urls.py中配置个人中心页面的url:

     1 from django.urls import path
     2 
     3 from .views import UserInfoView
     4 
     5 
     6 app_name = 'users'
     7 
     8 urlpatterns = [
     9     path('info/', UserInfoView.as_view(), name='user_info'),  # 个人中心
    10 ]

      修改个人中心页面中显示个人信息的代码:

      要完善个人信息接口,首先需要在form.py中加入个人信息的form表单验证:

    1 class UserInfoForm(forms.ModelForm):
    2     """个人信息表单验证"""
    3     class Meta:
    4         model = UserProfile
    5         fields = ['nick_name', 'gender', 'birthday', 'address', 'mobile'] 

      然后完善个人信息接口中更新个人信息的逻辑:

     1 class UserInfoView(View):
     2     """个人中心页面"""
     3     def get(self, request):
     4         return render(request, 'usercenter-info.html')
     5 
     6     def post(self, request):
     7         userinfo_form = UserInfoForm(request.POST, instance=request.user)
     8         if userinfo_form.is_valid():
     9             userinfo_form.save()
    10             return HttpResponse('{"status": "success"}', content_type='application/json')
    11         else:
    12             return HttpResponse(json.dumps(userinfo_form.errors), content_type='application/json')

      然后修改个人中心页面中显示个人信息的代码,加上{% csrf_token %}:

    1.3 修改用户头像接口

      首先在form.py文件中加入头像的ModelForm表单验证:

    1 class UploadImageForm(forms.ModelForm):
    2     """修改用户头像"""
    3     class Meta:
    4         model = UserProfile
    5         fields = ['image']

      然后完成修改用户头像的接口:

     1 class UploadImageView(LoginRequiredMixin, View):
     2     """用户头像修改"""
     3     def post(self, request):
     4         # 上传的文件都在request.FILES里面获取
     5         image_form = UploadImageForm(request.POST, request.FILES)
     6         if image_form.is_valid():
     7             # 将获取到的图片保存到数据库
     8             image = image_form.cleaned_data['image']
     9             request.user.image = image
    10             request.user.save()
    11 
    12             return HttpResponse('{"status": "success"}', content_type='application/json')
    13         else:
    14             return HttpResponse('{"status": "fail"}', content_type='application/json')

      配置url:

    1 from .views import UploadImageView
    2 
    3 
    4 urlpatterns = [
    5     path('image/upload', UploadImageView.as_view(), name='image_upload'),  # 修改头像
    6 ]

      修改个人中心页面显示用户头像的代码:

    1.4 修改密码接口

     1 class UpdatePwdView(LoginRequiredMixin, View):
     2     """修改密码"""
     3     def post(self, request):
     4         modify_form = ModifyPwdForm(request.POST)
     5         if modify_form.is_valid():
     6             # 从request中获取密码
     7             pwd1 = request.POST.get('password1', '')
     8             pwd2 = request.POST.get('password2', '')
     9             if pwd1 != pwd2:
    10                 return HttpResponse('{"status": "fail", "mag": "密码不一致"}', content_type='application/json')
    11 
    12             # 保存密码
    13             user = request.user
    14             user.password = make_password(pwd2)
    15             user.save()
    16 
    17             return HttpResponse('{"status": "success"}', content_type='application/json')
    18         else:
    19             return HttpResponse(json.dumps(modify_form.errors), content_type='application/json')

      配置url:

    1 from .views import UpdatePwdView
    2 
    3 urlpatterns = [
    4     path('update/pwd/', UpdatePwdView.as_view(), name='update_pwd'),  # 修改密码
    5 ]

      修改密码的ajax代码在deco-user.js文件中,现在只需要在usercenter-base.html页面中修改密码的form表单中加上{% csrf_token %}:

      修改完成之后需要重新登录,现在修改右上角的登录状态以及显示信息,在usercenter-base.html页面中:

      同时将base.html页面和org_base.html页面也修改了。

    1.5 发送邮箱验证码接口

      首先在邮箱验证码的model中添加SEND_CHOICES的选项,并将发送类型字段的长度改成30:

     1 class EmailVerifyRecord(models.Model):
     2     """邮箱验证码"""
     3     SEND_CHOICES = (
     4         ('register', '注册'),
     5         ('forget', '找回密码'),
     6         ('update_email', '修改邮箱')
     7     )
     8 
     9     code = models.CharField('验证码', max_length=20)
    10     email = models.EmailField('邮箱', max_length=50)
    11     send_type = models.CharField('发送类型', max_length=30, choices=SEND_CHOICES)
    12     send_time = models.DateTimeField('发送时间', default=datetime.now)
    13 
    14     class Meta:
    15         verbose_name = '邮箱验证码'
    16         verbose_name_plural = verbose_name

      迁移数据库。

     1 class SendEmailCodeView(LoginRequiredMixin, View):
     2     """发送邮箱验证码"""
     3     def get(self, request):
     4         # 从request中获取email
     5         email = request.GET.get('email', '')
     6 
     7         # 判断邮箱是否已经存在
     8         if UserProfile.objects.filter(email=email):
     9             return HttpResponse('{"email": "邮箱已存在"}', content_type='application/json')
    10 
    11         # 发送邮件
    12         send_register_email(email, 'update_email')
    13         return HttpResponse('{"status": "success"}', content_type='application/json')

      配置url:

    from .views import SendEmailCodeView
    
    urlpatterns = [
        path("sendemail_code/", SendEmailCodeView.as_view(),name='sendemail_code'),  # 发送邮箱验证码
    ]

      发送邮件的ajax代码在dec-user.js中,在utils/email_send.py中增加发送邮件的内容:

    1     if send_type == 'update_email':
    2         email_title = "知能网邮箱修改验证码"
    3         email_body = "你的邮箱验证码为{0}".format(code)
    4 
    5         send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
    6         if send_status:
    7             pass

    1.6 修改邮箱接口

     1 class UpdateEmailView(LoginRequiredMixin, View):
     2     """修改邮箱"""
     3     def post(self, request):
     4         # 从request中获取email和code
     5         email = request.POST.get('email', '')
     6         code = request.POST.get('code', '')
     7 
     8         # 在数据库中查找是否已有记录
     9         existed_records = EmailVerifyRecord.objects.filter(email=email, code=code, send_type='update_email')
    10         if existed_records:
    11             # 修改邮箱
    12             user = request.user
    13             user.email = email
    14             user.save()
    15 
    16             return HttpResponse('{"status": "success"}', content_type='application/json')
    17         else:
    18             return HttpResponse('{"email": "验证码无效"}', content_type='application/json')

      配置url:

    1 from .views import UpdateEmailView
    2 
    3 urlpatterns = [
    4     path("update_email/", UpdateEmailView.as_view(), name='update_email'),  # 修改邮箱
    5 ]

      修改邮箱的ajax代码在dec-user.js中,然后在usercenter-base.html中显示修改邮箱代码加上{% csrf_token %}:

     2、我的课程页面

    2.1 前端页面配置

      将前端页面usercenter-mycourse.html拷贝到templates下。

      然后继承usercenter-base.html页面,重写需要block的地方:

    2.2 我的课程接口

    1 class MyCourseView(LoginRequiredMixin, View):
    2     """我的课程页面"""
    3     def get(self, request):
    4         # 获取用户的课程
    5         user_courses = UserCourse.objects.filter(user=request.user)
    6 
    7         return render(request, 'usercenter-mycourse.html', {
    8             'user_courses': user_courses
    9         })

      配置url:

    1 urlpatterns = [
    2     path('mycourse/', MyCourseView.as_view(), name='mycourse'),  # 我的课程
    3 ]

      然后修改usercenter.html页面中跳转到我的课程页面的url:

      修改我的课程页面中显示我的课程的代码:

    3、我的收藏页面

    3.1 课程机构页面

      前端页面配置,先将usercenter-fav-org.html页面拷贝到templates下,继承usercenter-base.html页面,重写需要block的地方:

      我的收藏-机构接口编写:

     1 class MyFavOrgView(LoginRequiredMixin, View):
     2     """我的收藏 - 机构"""
     3     def get(self, request):
     4         # 存放用户收藏的机构对象
     5         org_list = []
     6 
     7         # 从UserFavorite中获取用户收藏机构的id
     8         fav_orgs = UserFavorite.objects.filter(user=request.user, fav_type=2)
     9 
    10         # 根据id将机构对象放到org_list中
    11         for fav_org in fav_orgs:
    12             org_id = fav_org.fav_id
    13             org = CourseOrg.objects.get(id=org_id)
    14             org_list.append(org)
    15 
    16         return render(request, 'usercenter-fav-org.html', {
    17             'org_list': org_list
    18         })

      配置url:

    1 urlpatterns = [
    2     path('myfav/org/', MyFavOrgView.as_view(), name="myfav_org"),  # 我的收藏 - 机构
    3 ]

      修改usercenter-base.html页面中跳转到我的收藏的url:

     

       修改我的收藏页面显示收藏机构的代码:

    3.2 授课教师页面

      前端页面配置,先将usercenter-fav-teacher.html页面拷贝到templates下,继承usercenter-base.html页面,重写需要block的地方:

      我的收藏 - 教师接口编写:

     1 class MyFavTeacherView(LoginRequiredMixin, View):
     2     """我的收藏 - 教师"""
     3     def get(self, request):
     4         teacher_list = []
     5         fav_teachers = UserFavorite.objects.filter(user=request.user, fav_type=3)
     6         for fav_teacher in fav_teachers:
     7             teacher_id = fav_teacher.fav_id
     8             teacher = Teacher.objects.get(id=teacher_id)
     9             teacher_list.append(teacher)
    10 
    11         return render(request, 'usercenter-fav-teacher.html', {
    12             'teacher_list': teacher_list
    13         })

      配置url:

    1 urlpatterns = [
    2     path('myfav/teacher/', MyFavTeacherView.as_view(), name="myfav_teacher"),  # 我的收藏 - 教师
    3 ]

      该页面需要显示教师课程的数量,需要在teacher的model中添加获取课程数量的函数:

     1 class Teacher(models.Model):
     2     """机构老师"""
     3     org = models.ForeignKey(CourseOrg, verbose_name='所属机构', on_delete=models.CASCADE)
     4     name = models.CharField('老师名', max_length=50)
     5     age = models.IntegerField('年龄', default=25)
     6     work_years =models.IntegerField('工作年限', default=0)
     7     work_company = models.CharField('就职公司', max_length=50)
     8     work_position = models.CharField('工作职位', max_length=50)
     9     points = models.CharField('教学特点', max_length=50)
    10     click_nums = models.IntegerField('点击数', default=0)
    11     fav_nums = models.IntegerField('收藏数', default=0)
    12     image = models.ImageField('头像', upload_to='teacher/%Y/%m', max_length=100, default='')
    13     add_time = models.DateTimeField('添加时间', default=datetime.now)
    14 
    15     class Meta:
    16         verbose_name = '教师'
    17         verbose_name_plural = verbose_name
    18 
    19     # 获取教师课程的数量
    20     def get_course_nums(self):
    21         return self.course_set.all().count()
    22 
    23     def __str__(self):
    24         return '[{}]机构的教师:{}'.format(self.org.name, self.name)

      在课程机构页面修改跳转到授课教师的url:

      修改授课教师页面中显示授课教师的代码:

    3.3 公开课程页面

      前端页面配置,先将usercenter-fav-course.html页面拷贝到templates下,继承usercenter-base.html页面,重写需要block的地方:

      我的收藏 - 课程接口编写:

     1 class MyFavCourseView(LoginRequiredMixin, View):
     2     """我的收藏 - 课程"""
     3     def get(self, request):
     4         course_list = []
     5         fav_courses = UserFavorite.objects.filter(user=request.user, fav_type=1)
     6         for fav_course in fav_courses:
     7             course_id = fav_course.fav_id
     8             course = Course.objects.get(id=course_id)
     9             course_list.append(course)
    10 
    11         return render(request, 'usercenter-fav-course.html', {
    12             'course_list': course_list
    13         })

      配置url:

    1 urlpatterns = [
    2     path('myfav/course/', MyFavCourseView.as_view(), name="myfav_course"),  # 我的收藏 - 课程
    3 ]

      修改公开课程页面显示收藏课程的代码:

      最后不要忘记修改各个页面之间跳转的url。

    4、我的消息页面

    4.1 前端页面配置

      将前端页面usercenter-message.html页面拷贝到templates下,继承usercenter-base.html页面,重写需要block的地方:

    4.2 我的消息接口

     1 class MyMessageView(LoginRequiredMixin, View):
     2     """我的消息页面"""
     3     def get(self, request):
     4         # 获取用户的消息
     5         all_message = UserMessage.objects.filter(user=request.user.id)
     6 
     7         # 分页
     8         try:
     9             page = request.GET.get('page', 1)
    10         except PageNotAnInteger:
    11             page = 1
    12         p = Paginator(all_message, 5, request=request)
    13         messages = p.page(page)
    14 
    15         return render(request, 'usercenter-message.html', {
    16             'messages': messages
    17         })

      配置url:

    1 urlpatterns = [
    2     path('my_message/', MyMessageView.as_view(), name="my_message"),  # 我的消息
    3 ]

      在usercenter-base.html页面中修改跳转到我的消息页面的url:

      修改我的消息页面显示消息的代码:

      分页显示:

    5、登出功能

      在views.py中编写登出的接口:

    1 class LogoutView(View):
    2     """登出功能"""
    3     def get(self, request):
    4         # 使用django的logout函数登出
    5         logout(request)
    6 
    7         # 将页面重定向到index
    8         return HttpResponseRedirect(reverse('index'))

      在MxOnline/urls.py中配置url:

    1 urlpatterns = [
    2     path('logout/', LogoutView.as_view(), name='logout'),  # 登出
    3 ]

      然后在三个base页面修改登出的url。

    6、点击数、收藏数

      在用户点击开始学习之后,学习人数需要加1,在课程章节接口CourseLessonView中完善加1操作:

     1 class CourseLessonView(LoginRequiredMixin, View):
     2     """课程章节"""
     3     def get(self, request, course_id):
     4         # 根据前端传递的课程id找到对应的课程
     5         course = Course.objects.get(id=int(course_id))
     6 
     7         # 学习人数+1
     8         course.students += 1
     9         course.save()
    10 
    11         # 课程和用户关联,先判断用户是否已经学习过该课程
    12         user_courses = UserCourse.objects.filter(user=request.user, course=course)
    13         if not user_courses:
    14             # 没有学习过该课程,关联
    15             user_course = UserCourse(user=request.user, course=course)
    16             user_course.save()
    17 
    18         # 获取所有的资源文件
    19         all_resources = CourseResourse.objects.filter(course=course)
    20 
    21         # 相关课程推荐
    22         # 找到学习这门课程的所有用户
    23         user_courses = UserCourse.objects.filter(course=course)
    24         # 找到学习这门课的所有用户的id
    25         user_ids = [user_course.user_id for user_course in user_courses]
    26         # 通过所有用户的id,找到所有用户学习过的所有课程
    27         all_user_courses = UserCourse.objects.filter(user_id__in=user_ids)
    28         # 取出所有课程id
    29         course_ids = [all_user_course.course_id for all_user_course in all_user_courses]
    30         # 通过所有课程id找到所有的课程,按点击量区5个
    31         relate_courses = Course.objects.filter(id__in=course_ids).order_by('-click_nums')[:5]
    32 
    33         return render(request, 'course-video.html', {
    34             'course': course,
    35             'all_resources': all_resources,
    36             'relate_courses': relate_courses
    37         })

      在点击教师进入教师详情页面,教师的点击数加1,在教师详情接口TeacherDetailView中完善加1逻辑:

     1 class TeacherDetailView(View):
     2     """讲师详情页面"""
     3     def get(self, request, teacher_id):
     4         # 根据前端的讲师id找到对应的讲师
     5         teacher = Teacher.objects.get(id=int(teacher_id))
     6 
     7         # 老师点击数加1
     8         teacher.click_nums += 1
     9         teacher.save()
    10 
    11         # 获取该老师所有的课程
    12         all_courses = Course.objects.filter(teacher=teacher)
    13 
    14         # 讲师排行
    15         teacher_sorted = Teacher.objects.all().order_by('-click_nums')[:3]
    16 
    17         # 讲师收藏、机构收藏
    18         has_teahcer_faved = False
    19         if UserFavorite.objects.filter(user=request.user, fav_type=3, fav_id=teacher_id):
    20             has_teahcer_faved = True
    21         has_org_faved = False
    22         if UserFavorite.objects.filter(user=request.user, fav_type=2, fav_id=teacher.org.id):
    23             has_org_faved = True
    24 
    25         return render(request, 'teacher-detail.html', {
    26             'teacher': teacher,
    27             'all_courses': all_courses,
    28             'teacher_sorted': teacher_sorted,
    29             'has_teahcer_faved': has_teahcer_faved,
    30             'has_org_faved': has_org_faved
    31         })

      在点击机构进入机构详情页,机构的点击数加1,在机构详情接口OrgHomeView中完善点击数加1逻辑:

     1 class OrgHomeView(View):
     2     """机构首页页面"""
     3     def get(self, request, org_id):
     4         # 根据前端的id找机构
     5         org = CourseOrg.objects.get(id=int(org_id))
     6 
     7         # 点击数加1
     8         org.click_nums += 1
     9         org.save()
    10 
    11         # 反向查询机构所有的课程和教师
    12         all_courses = org.course_set.all()
    13         all_teachers =org.teacher_set.all()
    14 
    15         # 标记
    16         current_page = 'home'
    17 
    18         # 判断收藏状态
    19         has_fav = False
    20         if request.user.is_authenticated:
    21             if UserFavorite.objects.filter(user=request.user, fav_id=org.id, fav_type=2):
    22                 has_fav = True
    23 
    24         return render(request, 'org-detail-homepage.html', {
    25             'org': org,
    26             'all_courses': all_courses,
    27             'all_teachers': all_teachers,
    28             'current_page': current_page,
    29             'has_fav': has_fav
    30         })

      用户在进行收藏和取消收藏时都需要对收藏数进行加和减的操作,在机构收藏接口OrgFavView中完善收藏数的逻辑:

     1 class OrgFavView(View):
     2     """机构收藏"""
     3     def post(self, request):
     4         # 从request中获取收藏的机构id和类型
     5         id = request.POST.get('fav_id', 0)
     6         type = request.POST.get('fav_type', 0)
     7 
     8         # 未登录返回json数据到前端,由前端进行登录页面的跳转
     9         if not request.user.is_authenticated:
    10             return HttpResponse('{"status": "fail", "msg": "用户未登录"}', content_type='application/json')
    11 
    12         # 在数据库中查找是否有过收藏记录:
    13         exist_record = UserFavorite.objects.filter(user=request.user, fav_id=int(id), fav_type=int(type))
    14         if exist_record:
    15             # 记录存在,取消收藏
    16             exist_record.delete()
    17 
    18             # 收藏数减1
    19             if int(type) == 1:
    20                 course = Course.objects.get(id=int(id))
    21                 course.fav_nums -= 1
    22                 if course.fav_nums < 0:
    23                     course.fav_nums = 0
    24                 course.save()
    25             elif int(type) == 2:
    26                 org = CourseOrg.objects.get(id=int(id))
    27                 org.fav_nums -= 1
    28                 if org.fav_nums < 0:
    29                     org.fav_nums = 0
    30                 org.save()
    31             elif int(type) == 3:
    32                 teacher = Teacher.objects.get(id=int(id))
    33                 teacher.fav_nums -= 1
    34                 if teacher.fav_nums < 0:
    35                     teacher.fav_nums = 0
    36                 teacher.save()
    37             return HttpResponse('{"status": "success", "msg": "收藏"}', content_type='application/json')
    38         else:
    39             # 记录不存在,收藏
    40             user_fav = UserFavorite()
    41             if int(id)>0 and int(type)>0:
    42                 user_fav.user = request.user
    43                 user_fav.fav_id = int(id)
    44                 user_fav.fav_type = int(type)
    45                 user_fav.save()
    46 
    47                 # 收藏数
    48                 if int(type) == 1:
    49                     course = Course.objects.get(id=int(id))
    50                     course.fav_nums += 1
    51                     course.save()
    52                 elif int(type) == 2:
    53                     org = CourseOrg.objects.get(id=int(id))
    54                     org.fav_nums += 1
    55                     org.save()
    56                 elif int(type) == 3:
    57                     teacher = Teacher.objects.get(id=int(id))
    58                     teacher.fav_nums += 1
    59                     teacher.save()
    60                 return HttpResponse('{"status": "success", "msg": "已收藏"}', content_type='application/json')
    61             else:
    62                 return HttpResponse('{"status": "fail", "msg": "收藏出错"}', content_type='application/json')

    7、个人中心页面左侧选中状态

      现在在usercenter-base.html页面中修改左侧导航栏选中状态的代码:

      还有一个问题,就是我的收藏中删除机构收藏,需要修改usercenter-base.html页面中最下面的代码:

      下面的教师和课程也是同样的改法。

关键字