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

2018-11-26 07:58:36来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

十五、首页全局配置

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页面中最下面的代码:

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

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:python轻量级数据存储

下一篇:python深拷贝浅拷贝