十五、首页全局配置
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" >新 密 码</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">密 码</span> 200 <input type="password" name="password" id="jsResetPwd" placeholder="请输入新密码"/> 201 </div> 202 <div class="box"> 203 <span class="word2">确 认 密 码</span> 204 <input type="password" name="password2" id="jsResetPwd2" placeholder="请再次输入新密码"/> 205 </div> 206 <div class="box"> 207 <span class="word2">验 证 码</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" >账 号</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" /> <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-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页面中最下面的代码:
下面的教师和课程也是同样的改法。