将index.html继承base.html页面,修改继承的block地方:
修改base.html页面中导航栏选中状态的代码:
首页的全局搜索功能可以对课程,机构,教师进行全局搜索,搜索的代码放在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 })
讲师列表接口
在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-base.html
然后将usercenter-info.html页面继承母版:
在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 %}:
首先在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 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页面也修改了。
首先在邮箱验证码的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 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 %}:
将前端页面usercenter-mycourse.html拷贝到templates下。
然后继承usercenter-base.html页面,重写需要block的地方:
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:
修改我的课程页面中显示我的课程的代码:
前端页面配置,先将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:
修改我的收藏页面显示收藏机构的代码:
前端页面配置,先将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:
修改授课教师页面中显示授课教师的代码:
前端页面配置,先将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。
将前端页面usercenter-message.html页面拷贝到templates下,继承usercenter-base.html页面,重写需要block的地方:
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:
修改我的消息页面显示消息的代码:
分页显示:
在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。
在用户点击开始学习之后,学习人数需要加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')
现在在usercenter-base.html页面中修改左侧导航栏选中状态的代码:
还有一个问题,就是我的收藏中删除机构收藏,需要修改usercenter-base.html页面中最下面的代码:
下面的教师和课程也是同样的改法。