美文网首页零基础使用Django2.0.1打造在线教育网站
零基础使用Django2.0.1打造在线教育网站(二十二):个人

零基础使用Django2.0.1打造在线教育网站(二十二):个人

作者: 啃饼小白 | 来源:发表于2018-08-16 07:39 被阅读2次

    写在前面

    本篇笔记我们将介绍用户个人资料页面的配置,具体包括全局导航栏配置,全局搜索功能开发,个人中心资料展示,修改密码和头像以及页面表单提交等功能,下面我们依次介绍一下。

    本篇笔记对应于第二十二篇代码,对应于github的位置是https://github.com/licheetools/eduline

    全局导航栏配置

    首先修改index.html页面,让它也继承我们的base.html页面:


    然后打开base.html.html页面,我们做一下页面的链接跳转:
    <div class="nav">
                        <div class="wp">
                            <ul>
                                <li ><a href="{% url 'index' %}">首页</a></li>
                                <li >
                                    <a href="{% url 'course:course_list' %}">
                                        公开课<img class="hot" src="{% static 'images/nav_hot.png' %}">
                                    </a>
                                </li>
                                <li >
                                    <a href="{% url 'org:teacher_list' %}">授课教师</a>
                                </li>
                                <li class="active" ><a href="{% url 'org:org_list' %}">授课机构</a></li>
                            </ul>
                        </div>
                    </div>
    

    这样跳转链接算是弄好了,但是我们暂时还不知道根据什么来判断当前页面已经被选中,所以我们需要仿照之前在课程机构里面的那样,增加一个current_nav用于指明当前页面的代号然后就可以根据某值是否相等来判断页面是否选中的状态了。

    # organizaton/views.py里面
    class TeacherListView(View):
    
    current_nav = "teacher"
    
     return render(request, "teachers-list.html", {
                "current_nav": current_nav,
            })
    
    # base.html页面里面
    <li {% if current_nav == 'teacher' %}class="active"{% endif %}>
    <a href="{% url 'org:teacher_list' %}">授课教师</a>
    </li>
    
    上述方法的确可以使用,但是有一个问题,就是为了满足前端页面的显示,你在后端每个涉及到的view里面都必须有current view,这是不是太麻烦了?这只是一级导航栏少而且没有二三级导航栏的情况下才可以,多了完全是不可以的。你看一下淘宝的页面导航栏:

    那么问题来了,又什么好方法可以解决这个问题吗?答案是有的!

    我们可以根据request.path中的前几位来判断当前处于何种页面,从而选中何种状态。你可能会糊涂,别急我举个例子给你说明一下:
    http://127.0.0.1:8000/ 你肯定知道访问的是根目录; http://127.0.0.1:8000/course/detail/2你肯定也知道访问的是课程相关的页面;
    http://127.0.0.1:8000/org/teacher/detail你肯定也知道访问的是讲师相关的页面;
    鉴于此,我们就是采用这种方式来判断选中状态的。打开base.html.html页面,修改代码如下:

    <ul>
                                <li {% if request.path == "/" %}class="active"{% endif %}>
                                    <a href="{% url 'index' %}">首页</a>
                                </li>
                                <li {% if request.path|slice:"7" == "/course" %}class="active"{% endif %}>
                                    <a href="{% url 'course:course_list' %}">
                                        公开课<img class="hot" src="{% static 'images/nav_hot.png' %}">
                                    </a>
                                </li>
                                <li {% if request.path|slice:"12" == "/org/teacher" %}class="active"{% endif %}>
                                    <a href="{% url 'org:teacher_list' %}">授课教师</a>
                                </li>
                                <li {% if request.path|slice:"9" == "/org/list" %}class="active"{% endif %} >
                                    <a href="{% url 'org:org_list' %}">授课机构</a>
                                </li>
                            </ul>
    

    其中request.path是指访问的全路径地址,if request.path|slice:"12" == "/org/teacher"是指除了域名以外(此处域名为127.0.0.1),从1-12位的地址等于/org/teacher即访问地址为:

    http://127.0.0.1:8000/org/teacher
    
    就是这个样子:

    运行一下我们的项目,发现是随着我们点哪个,哪个页面就是被选中状态。

    全局搜索功能开发

    所谓全局搜索功能,其实就是在你指定的搜索范围内,为你查找符合你所要求的信息:

    打开courses/views.py文件,在CourseListView里面,新增以下代码:

    from django.db.models import Q
    
    # 搜索功能
            search_keywords =  request.GET.get('keywords', '')
            if search_keywords:
                all_courses = all_courses.filter(Q(name__icontains=search_keywords)| Q(desc__icontains=search_keywords)| Q(detail__icontains=search_keywords))
    
     return render(request, "course-list.html", {
                "search_keywords": search_keywords,
            })
    

    注意:name__icontains表示姓名中含有某个字段,不区分大小写,而name__contains则区分大小写。还有我们的搜索功能必须在排序之前进行,否则就得不到我们想要的数据了。

    就是这个样子:
    实现的Js代码已经放在了我们的deco-commmon.js里面:
    同样把机构,讲师的view也做同样的操作:
    注意一下,search_keywords都要render回对应的函数里去,还有Q里面的选择字段是根据你在其对应的数据库中对应的字段,不是随意写的,所以每个都是不一样的。

    运行一下我们的项目,发现搜索功能正常,可以随意搜索。

    个人中心信息展示

    老规矩,把前端资料里面的6个与个人中心相关(usercenter_xxx.html)的html页面拷贝到我们的templates文件夹里面,接着新建一个usercenter_base.html页面,新增一些代码:


    注意,考虑到篇幅的原因,全部代码请大家到我的github上查看!

    然后修改usercenter_info.html页面:


    接着配置users的全局urls的入口,打开eduline/urls.py文件,新增以下代码:
     # 用户个人中心应用path配置
        path("users/", include('users.urls', namespace="users")),
    

    然后在users文件夹下面,新建urls.py文件,里面输入以下代码:

    
    from django.urls import path, include, re_path
    from .views import UserInfoView
    
    
    app_name = "users"
    
    urlpatterns = [
        # 用户信息页url
        path("info/", UserInfoView .as_view(), name="user_info"),
    
    ]
    

    接着打开users/views.py文件,在其底下新增以下代码:

    # 用户个人信息
    class UserInfoView(LoginRequiredMixin , View):
        login_url = '/login/'
        redirect_field_name = 'next'
    
        def get(self, request):
            return render(request, "usercenter-info.html", {
    
            })
    

    配置完以后,我们运行一下我们的项目,在浏览器地址栏中输入:http://127.0.0.1:8000/users/info/,发现页面已经出来了:


    {{ request.user.address|default_if_none:'' }}表示默认为空

    然后打开usercenter_info.html页面,找到并修改以下代码为下图所示(看不清楚的小伙伴们请移步到我的github上面,查看源代码):


    接下来我们完成用户个人中心的密码和头像修改。

    用户头像的修改

    老规矩,先配置path,打开users/urls.py文件,新增以下代码:

    from .views import  ImageUploadView
    
      # 用户上传图片url
        path("image/upload/", ImageUploadView .as_view(), name="image_upload"),
    
    

    然后打开users/views.py文件,新增以下代码:

    
    # 用户头像修改
    class ImageUploadView(LoginRequiredMixin, View):
        login_url = '/login/'
        redirect_field_name = 'next'
    
        def post(self, request):
            return render(request, "usercenter-info.html" ,{
    
            })
    

    正如你所知道的,但凡涉及到了数据的提交都会用到post方法以及form表单验证。我们之前采用Django的modleform来对表单进行重载,而且定义的都是普通的字段。但是这里我们定义的却是图片这种字段,那么我们还能使用这种方法么?答案是可以的!因为在Django的xadmin和admin当中,实际上是可以当form被定义为文件的时候,可以自动对上传的文件做保存的。所以我们可以使用form的一个字段来定义一个文件类型,那么该字段取出来其实就是取出了里面存储的文件。

    打开users/forms.py文件,新增以下代码:

    from .models import UserProfile
    
    # 个人用户修改头像
    class ImageUploadForm(forms.ModelForm):
        # 除了继承现有的字段还可以新增字段
        class Meta:
            model = UserProfile
            # 自定义需要验证的字段
            fields = ["image"]
    

    然后打开users/views.py文件,新增并修改ImageUploadView函数:

    from .forms import ImageUploadForm
    
    
    
    # 用户头像修改
    class ImageUploadView(LoginRequiredMixin, View):
        login_url = '/login/'
        redirect_field_name = 'next'
    
        def post(self, request):
            image_form = ImageUploadForm(request.POST, request.FILES)
            if image_form.is_valid():
                pass
    

    接着我们打开usercenter_info.html页面,修改图中信息为图所示:


    注意:enctype的值必须为multipart/form-data,提交类型为文件(type="file")只有满足这两个条件才能把文件类型传递到后台并进行存储!

    接下来,我们在图示位置打上断点,开启debug模式:



    上传的文件都在request.FILES这个里面,这个image就是我们选择上传的图片,看到没有我们上传的图片名称是4b637875ef808d285701bb898abb6fe9.jpg

    然后验证通过的文件会存放到cleaned_data里面,记住这是一个dict,我们现在需要取出里面的图片并进行保存。修改我们的ImageUploadView函数:
    # 用户头像修改
    class ImageUploadView(LoginRequiredMixin, View):
        login_url = '/login/'
        redirect_field_name = 'next'
    
        def post(self, request):
            image_form = ImageUploadForm(request.POST, request.FILES)
            if image_form.is_valid():
                image = image_form.cleaned_data['image']
                request.user.image = image
                request.user.save()
                pass
    

    是不是很简单,不过呢还有比这更简单的,上面那种是常规的做法,我们这里既然采用了modleform,就要用起来,修改代码如下:

    # 用户头像修改
    class ImageUploadView(LoginRequiredMixin, View):
        login_url = '/login/'
        redirect_field_name = 'next'
    
        def post(self, request):
            image_form = ImageUploadForm(request.POST, request.FILES, instance=request.user)
            if image_form.is_valid():
                image_form.save()
                pass
    

    最后对成功与否我们进行页面的一个返回,修改代码如下:

    from django.http import HttpResponse
    
    # 用户头像修改
    class ImageUploadView(LoginRequiredMixin, View):
        login_url = '/login/'
        redirect_field_name = 'next'
    
        def post(self, request):
            image_form = ImageUploadForm(request.POST, request.FILES, instance=request.user)
            if image_form.is_valid():
                image_form.save()
                return HttpResponse('{"status":"success"}', content_type='application/json')
            else:
                return HttpResponse('{"status":"fail"}', content_type='application/json')
    

    至此用户头像修改的功能已经介绍完毕,下面我们介绍用户密码的修改。

    用户密码的修改

    打开users/urls.py文件,新建代码:

    from .views import  UpdatePwdView
    
     # 用户个人中心修改密码url
        path("update/pwd/", UpdatePwdView.as_view(), name="update_pwd"),
    
    

    然后打开users/views.py文件,新增以下代码:

    from django.http import HttpResponse
    
    # 用于个人中心修改密码的函数(已经登录)
    class UpdatePwdView(View):
        def post(self, request):
            modify_form = ModifyPwdForm(request.POST)
            if modify_form.is_valid():
                pwd1 = request.POST.get("password1", '')
                pwd2 = request.POST.get("password2", '')
                # 如果前后两次密码不相等,那么回填信息并返回错误提示
                if pwd1 != pwd2:
                    return HttpResponse('{"status":"fail", "msg":"密码不一致"}', content_type='application/json')
                # 如果前后两次密码相等,那么进入我们的密码修改保存
                # 取出用户信息
                user = request.user
                # 随意取出一个密码并将其进行加密
                user.password = make_password(pwd1)
                # 将更新后的用户信息保存到数据库里面
                user.save()
                # 密码重置成功以后,跳转到登录页面
                return HttpResponse('{"status":"success"}', content_type='application/json')
            else:
                return HttpResponse('{"status":"fail", "msg":"填写错误请检查"}', content_type='application/json')
    
    
    接着打开deco-user.js文件,看到这里:

    注意,因为这是单独的js文件,所以我们url的配置不能和在我们的templates里面的模板一样,采用{% url '' %}这种形式,而是要采用通用的/x/x方式。

    在图示位置打上断点开启debug模式:


    然后在浏览器地址栏中输入http://127.0.0.1/users/info,尝试修改密码,发现密码修改没有问题。很简单,这里就不一一演示了。

    然后打开base.html页面,对登录状态进行判断:修改这段代码为图示:

    {% if request.user.is_authenticated %}
                        <div class="wp">
                            <div class="fl"><p>服务电话:<b>23333333</b></p></div>
                            <div class="personal">
    
                                <dl class="user fr">
                                    <dd>{{ request.user.username }}<img class="down fr" src="{% static 'images/top_down.png' %}"/>
                                    </dd>
                                    <dt><img width="20" height="20" src="{{ MEDIA_URL }}{{ request.user.image }}"/></dt>
                                </dl>
                                <div class="userdetail">
                                    <dl>
                                        <dt><img width="80" height="80" src="{{ MEDIA_URL }}{{ request.user.image }}"/></dt>
                                        <dd>
                                            <h2>{{ request.user.nick_name }}</h2>
                                            <p>{{ request.user.username }}</p>
                                        </dd>
                                    </dl>
                                    <div class="btn">
                                        <a class="personcenter fl" href="{% url 'users:user_info' %}">进入个人中心</a>
                                        <a class="fr" href="">退出</a>
                                    </div>
                                </div>
                            </div>
                            <a href="">
                                <div class="msg-num"><span id="MsgNum">{{ request.user.unread_nums }}</span></div>
                            </a>
                        </div>
                    {% else %}
    
                        <div class="wp">
                            <div class="fl"><p>服务电话:<b>33333333</b></p></div>
                            <!--登录后跳转-->
    
                            <a style="color:white" class="fr registerbtn" href="{% url 'register' %}">注册</a>
                            <a style="color:white" class="fr loginbtn"
                               href="{% url 'login' %}?next={{ request.path }}">登录</a>
    
    
                        </div>
                    {% endif %}
    
    

    它在base.html的位置是在<div class="top">之间,就是这个位置:


    然后复制这个折叠起来的{% if request.user.is_authenticated %}到org_base.html和usercenter_base.html页面里去,放置的位置也是一模一样的,这里就不再演示了。 然后刷新我们的页面:

    首页显示没有问题!



    公开课显示没有问题!

    授课讲师显示没有问题!



    授课机构显示没有问题!

    个人中心显示没有问题!

    修改用户邮箱

    我们点击右侧的修改邮箱就跳出一个弹窗,提示我们输入重新绑定的邮箱,并且需要发送邮箱验证码对其进行验证:

    获取验证码

    打开users/urls.py文件,新增代码:

    from .views import SendEmailCodeView
    
     # 用户个人中心发送邮箱验证码url
        path("sendemail_code/", SendEmailCodeView.as_view(), name="sendemail_code"),
    
    

    然后打开users/views.py文件,新增代码如下:

    # 用于个人中心发送邮箱验证码的函数
    class SendEmailCodeView(LoginRequiredMixin, View):
        login_url = '/login/'
        redirect_field_name = 'next'
    
        def get(self, request):
            # 取出待发送的邮件
            email = request.GET.get("email", '')
    
            if UserProfile.objects.filter(email=email):
                return HttpResponse('{"email":"邮箱已经存在"}', content_type='application/json')
            send_register_eamil(email, 'update_email')
            return HttpResponse('{"status":"success"}', content_type='application/json')
    

    接着打开utils/email_send.py文件,新增以下代码:

    
        elif send_type == "update_email":
            email_title = "慕海学习网邮箱修改验证链接"
            email_body = "亲,你的邮箱验证码为: http://127.0.0.1:8000/reset/{0}".format(code)
    
            send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
            if send_status:
                pass
    
    

    再来打开deco-user.js文件,看到这里:

    //修改个人中心邮箱验证码
    function sendCodeChangeEmail($btn){
        var verify = verifyDialogSubmit(
            [
              {id: '#jsChangeEmail', tips: Dml.Msg.epMail, errorTips: Dml.Msg.erMail, regName: 'email', require: true}
            ]
        );
        if(!verify){
           return;
        }
        $.ajax({
            cache: false,
            type: "get",
            dataType:'json',
            url:"/users/sendemail_code/",
            data:$('#jsChangeEmailForm').serialize(),
            async: true,
            beforeSend:function(XMLHttpRequest){
                $btn.val("发送中...");
                $btn.attr('disabled',true);
            },
            success: function(data){
                if(data.email){
                    Dml.fun.showValidateError($('#jsChangeEmail'), data.email);
                }else if(data.status == 'success'){
                    Dml.fun.showErrorTips($('#jsChangeEmailTips'), "邮箱验证码已发送");
                }else if(data.status == 'failure'){
                     Dml.fun.showValidateError($('#jsChangeEmail'), "邮箱验证码发送失败");
                }else if(data.status == 'success'){
                }
            },
            complete: function(XMLHttpRequest){
                $btn.val("获取验证码");
                $btn.removeAttr("disabled");
            }
        });
    
    }
    
    

    还是注意里面的url的配置是普通的url书写格式就可以了。

    然后打开usercenter_base.html页面,我们查找一下,在所以有form表单提交的地方都加上{% csrf_token %},里面有很多,慢慢仔细的查找,不要有遗漏。

    然后按照图示,开启debug模式:



    然后打开邮箱,就可以找到这个邮件了。



    接下来就是验证码的激活操作了。

    验证码激活

    打开users/urls.py文件,新增代码:

    from .views import UpdateEmailView
    
     # 用户个人中心修改邮箱url
        path("update_email/", UpdateEmailView.as_view(), name="update_email"),
    

    然后打开users/views.py文件,新增代码如下:

    # 用于个人中心修改邮箱的函数
    class UpdateEmailView(LoginRequiredMixin, View):
        login_url = '/login/'
        redirect_field_name = 'next'
    
        def post(self, request):
            email = request.POST.get("email", '')
            code = request.POST.get("code", '')
    
            existed_records = EmailVerifyRecord.objects.filter(email=email, code=code, send_type="update_email")
            if existed_records:
                # request.user.email = email
                user = request.user
                user.email = email
                user.save()
                return HttpResponse('{"status":"success"}', content_type='application/json')
            else:
                return HttpResponse('{"email":"验证码无效"}', content_type='application/json')
    
    
    EmailVerifyRecord中的字段有email,code,还有send_type,不记得话可以打开数据库,查看这张表:

    再来打开deco-user.js文件,看到这里:

    //个人资料邮箱修改
    function changeEmailSubmit($btn){
    var verify = verifyDialogSubmit(
            [
              {id: '#jsChangeEmail', tips: Dml.Msg.epMail, errorTips: Dml.Msg.erMail, regName: 'email', require: true},
            ]
        );
        if(!verify){
           return;
        }
        $.ajax({
            cache: false,
            type: 'post',
            dataType:'json',
            url:"/users/update_email/ ",
            data:$('#jsChangeEmailForm').serialize(),
            async: true,
            beforeSend:function(XMLHttpRequest){
                $btn.val("发送中...");
                $btn.attr('disabled',true);
                $("#jsChangeEmailTips").html("验证中...").show(500);
            },
            success: function(data) {
                if(data.email){
                    Dml.fun.showValidateError($('#jsChangeEmail'), data.email);
                }else if(data.status == "success"){
                    Dml.fun.showErrorTips($('#jsChangePhoneTips'), "邮箱信息更新成功");
                    setTimeout(function(){location.reload();},1000);
                }else{
                     Dml.fun.showValidateError($('#jsChangeEmail'), "邮箱信息更新失败");
                }
            },
            complete: function(XMLHttpRequest){
                $btn.val("完成");
                $btn.removeAttr("disabled");
            }
        });
    }
    
    

    现在你可以去调试一下验证码激活,看是不是成功了,很简单,这里就不介绍了。

    接下来我们是完成整个页面表单的提交,你总不能不把修改好的数据不存到数据库里面吧。我们继续采用我们的modelform,来完善我们表单的提交。

    页面表单提交

    打开users/forms.py文件,新增以下代码:

    # 用户信息修改表单提交
    class UserInfoForm(forms.ModelForm):
        # 除了继承现有的字段还可以新增字段
        class Meta:
            model = UserProfile
            # 自定义需要验证的字段,注意与数据库中字段应保持一致
            fields = ["nick_name", "birday", "gender", "address", "mobile"]
    

    然后打开我们的users/views.py文件,找到我们之前定义的userinfoview这个函数,我们新增它的post方法:

    from .forms import UserInfoForm
    import json
    
     def post(self, request):
            user_info_form = UserInfoForm(request.POST, instance=request.user)
            if user_info_form.is_valid():
                user_info_form.save()
                return HttpResponse('{"status":"success"}', content_type='application/json')
            else:
                return HttpResponse(json.dumps(user_info_form.errors), content_type='application/json')
    

    然后尝试提交一下你刚才修改过的表单,发现提交没有问题。

    至此,本篇关于用户个人资料页面及搜索框的配置就到此为止,感谢你的赏阅。

    本篇笔记对应于第二十二篇代码,对应于github的位置是https://github.com/licheetools/eduline

    相关文章

      网友评论

        本文标题:零基础使用Django2.0.1打造在线教育网站(二十二):个人

        本文链接:https://www.haomeiwen.com/subject/tkoubftx.html