美文网首页
注册登录首页展示实现

注册登录首页展示实现

作者: 莫辜负自己的一世韶光 | 来源:发表于2018-11-27 20:13 被阅读0次

    首页和登录页面的配置

    初始资源文件下载

    访问我们事先准备好的资源文件中的html文件


    在html中找到首页的html,拷贝到我们的template目录下

    新建static目录,用来存放css,js,图片等静态资源

    配置处理静态文件的url,主项目下的urls.py中

    rlpatterns = [
        url(r'^xadmin/', xadmin.site.urls),
        # TemplateView.as_view()会将template转换为view
        url(r'^$', TemplateView.as_view(template_name='index.html'), name='index')
    ]
    

    在settings.py中设置静态文件的目录

    # 说明静态文件放在哪个目录
    
    STATIC_URL = '/static/'
    
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, "static"),
    )
    

    修改index页面中样式还有一些静态资源的引用地址

    全部换位/static/目录下

    然后点击运行,刷新页面就可以看到我们的首页显示了.

    拷贝登录页面到template

    替换css/js等静态资源文件.

    url配置跳转登录界面

     # 登录页面跳转url
        url('^login/$', TemplateView.as_view(template_name="login.html"), name="login")
    

    在index页面,ctrl+f找到登录.将a标签中的地址替换为login的url


    此时我们的首页已经可以成功显示了,通过首页点击登录可以成功跳转到登录界面

    用户登录

    配置url之前我们先书写好对应处理的view
    Django的view实际上是一个函数,接收request请求对象,处理后返回response对象.

    users/views.py

    from django.shortcuts import render
    
    
    def login(request):
        # POST请求
        if request.method == "POST":
            pass
        # GET请求
        elif request.method == 'GET':
            return render(request, 'login.html', {})
    
    

    urls.py

    from users.views import user_login
        # 登录页面跳转url login不要直接调用。而只是指向这个函数对象。
        url('^login/$',login, name="login")
    

    断点调试

    在两行返回语句的位置打上断点:


    点击debug,进入首页后点击登录.可以看到下面所示


    通过值浏览器窗口可以看到这是一个<WSGIRequest: GET '/login/'>对象

    path: 指向地址
    f8继续运行,跳转到登录页面.

    用户登录,通过form进行验证

    templates/login.html

    修改action='/login/' method = 'post'

    input中的name值将会被传递到后台.以键值对的形式.

    只保留post这里的断点.用户输入用户名密码.查看debug情况.

    然后访问,会发生如下的错误

    这是因为html页面内必须加上csrf_token才能传递给后台

    服务器会随机的给前端发一串符号,你必须使用{% csrf_token %}将字符串带回来,才允许你使用post

    django中写form表单时csrf_token的作用

    此时我们查看前端页面:


    可以看到html登录下面有一个隐藏着的值:csrf token,不会显示
    此时点击登录跳转到pass位置


    可以看到request中的POST是一个queryset对象.我们可以把它当成一个字典来用.
    来取到前端的数据,然后通过Django自带的auth方法进行验证.

    from django.contrib.auth import authenticate, login
    
        # 登录提交表单为post
        if request.method == "POST":
            # 取不到时为空,username,password为前端页面name值
            user_name = request.POST.get("username", "")
            pass_word = request.POST.get("password", "")
    
            # 成功返回user对象,失败返回null
            user = authenticate(username= user_name, password= pass_word)
    
            # 如果不是null说明验证成功
            if user is not None:
                # login_in 两参数:request, user
                # 实际是对request写了一部分东西进去,然后在render的时候:
                # request是要render回去的。这些信息也就随着返回浏览器。完成登录
                login(request, user)
                # 跳转到首页 user request会被带回到首页
                return render(request, "index.html")
            # 没有成功说明里面的值是None,并再次跳转回主页面
            else:
                return render(request, "login.html", {})
    

    authenticate调用只需要传入用户名和密码。成功会返回user对象,失败返回null

    注意:
    html中首页的展示通过用户是否登录来进行判断

    设置成功如果登录显示个人中心那段,未登录显示登录注册

    改造为使用邮箱用户名均可.

    方法就是使用自定义的authenticate方法,通过创建一个类CustomBackend 继承自ModelBackend

    rom django.contrib.auth.backends import ModelBackend
    from .models import UserProfile
    # 并集运算
    from django.db.models import Q
    
    # 实现用户名邮箱均可登录
    # 继承ModelBackend类,因为它有方法authenticate,可点进源码查看
    class CustomBackend(ModelBackend):
        def authenticate(self, username=None, password=None, **kwargs):
            try:
                # 不希望用户存在两个,get只能有一个。两个是get失败的一种原因 Q为使用并集查询
    
                user = UserProfile.objects.get(Q(username=username)|Q(email=username))
    
                # django的后台中密码加密:所以不能password==password
                # UserProfile继承的AbstractUser中有def check_password(self, raw_password):
    
                if user.check_password(password):
                    return user
            except Exception as e:
                return None
    

    settings.py中

    # 设置邮箱和用户名均可登录
    AUTHENTICATION_BACKENDS = (
        'users.views.CustomBackend',
    )
    

    用户提示:return页面时提供它的错误信息

        return render(request, 'login.html', {"msg": '用户名或密码错误'})
    

    html中如何取到这个值

    <div class="error btns login-form-tips" id="jsLoginTips">{{ msg }}</div>
    

    将以前的fbv(基于函数的视图)改成cbv(基于类的视图)

    # 登录视图类
    class LoginView(View):
        def get(self, request):
            # render就是渲染html返回用户
            return render(request, 'login.html', {})
    
        def post(self, request):
            # 取不到的时候为空,username,password为前端页面标签元素的name属性值.
            username = request.POST.get('username', '')
            password = request.POST.get('password', '')
            # 使用auth模块的authenticate方法进行验证,成功返回user,失败返回null
            user = authenticate(username=username, password=password)
    
            # 如果不是null
            if user:
                # auth模块的login方法,两个参数request,user
                # 实际上是对request写了一部分东西进去,然后在render的时候:
                # request是要render回去的.这些信息也就随着返回给浏览器,完成登录
                login(request, user)
                # 跳转到首页 user,request会被带回到首页
                return render(request, 'index.html')
            else:
    

    我们去看源码View里面的请求方法有

        http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    

    对各个方法的解释

    patch方法介绍

    urls.py中换用类实现

        url(r'^login/$', LoginView.as_view(), name='index'),
    

    form字段验证

    验证最大长度,是否为空等一系列,去数据库查询之前的一些对比

    user.py下新建forms文件

    def post(self, request):
            # 类实例化需要一个字典参数dict:request.POST就是一个QueryDict所以直接传入
            # POST中的usernamepassword,会对应到form中
            login_form = LoginForm(request.POST)
            #is_valid判断我们字段是否有错执行我们原有逻辑,验证失败跳回login页面
            if login_form.is_valid():
                # 取不到时为空,username,password为前端页面name值
                user_name = request.POST.get("username", "")
                pass_word = request.POST.get("password", "")
    
                # 成功返回user对象,失败返回null
                user = authenticate(username=user_name, password=pass_word)
    
                # 如果不是null说明验证成功
                if user is not None:
                    # login_in 两参数:request, user
                    # 实际是对request写了一部分东西进去,然后在render的时候:
                    # request是要render回去的。这些信息也就随着返回浏览器。完成登录
                    login(request, user)
                    # 跳转到首页 user request会被带回到首页
                    return render(request, "index.html")
            # 验证不成功跳回登录页面
            # 没有成功说明里面的值是None,并再次跳转回主页面
            else:
                return render(request, "login.html", {"msg": "用户名或密码错误! "})
    

    错误提示的时候,表单验证失败之后,就没必要提示用户名或密码错误了

    并且将form传回前端:

    # 登录视图类
    class LoginView(View):
        def get(self, request):
            # render就是渲染html返回用户
            return render(request, 'login.html', {})
    
        def post(self, request):
            # 类实例化需要一个字典参数dict:request.POST就是一个QueryDict所以直接传入
            # POST中的usernamepassword,会对应到form中
            login_form = LoginForm(request.POST)
            # is_valid判断我们字段是否有执行我们的原有的逻辑,验证失败跳回到login页面
            if login_form.is_valid():
                # 取不到的时候为空,username,password为前端页面标签元素的name属性值.
                username = request.POST.get('username', '')
                password = request.POST.get('password', '')
                # 使用auth模块的authenticate方法进行验证,成功返回user,失败返回null
                user = authenticate(username=username, password=password)
    
                # 如果不是null
                if user:
                    # auth模块的login方法,两个参数request,user
                    # 实际上是对request写了一部分东西进去,然后在render的时候:
                    # request是要render回去的.这些信息也就随着返回给浏览器,完成登录
                    login(request, user)
                    # 跳转到首页 user,request会被带回到首页
                    return render(request, 'index.html')
                else:
                    return render(request, 'login.html', {"msg": '用户名或密码错误'})
            else:
                return render(request, 'login.html', {'login_form': login_form})
    

    前端中取值

    image.png

    给它们加上errorput会显示红色外框

    将forms错误信息显示出来

    <div class="error btns login-form-tips" id="jsLoginTips">
    {% for key, error in login_form.errors.items %}
    {{ error }}
    {% endfor %}
    {{ msg }}</div>
    
    • 写了一个类继承Django的View,然后写了get,post方法(get/post的if是Django替我们完成的 )
    • url中调用了LoginView的as_view方法需要加上括号,进行调用
    • Django的form进行表单验证并把error值传递到前台
    • is_vilid方法,验证表单

    session和cookie的自动登录机制

    cookie的存储

    cookie是浏览器支持的一种本地存储方式.以dict,键值对方式存储.

    {"sessionkey": "123"}
    

    浏览器会自动对于它进行解析.

    http请求是一种无状态的请求

    用户向服务器发起的两次请求之间是没有状态的.也就是说服务器并不知道这是同一个用户发的.

    做到记住用户:

    浏览器a在向服务器发起请求,服务器会自动给浏览器a回复一个session_id,浏览器a把id
    保存到cookie当中,在下一次请求时带上这个cookie里的id值向浏览器请求
    服务器就知道你是哪个浏览器发送过来的了.

    有状态请求示例:

    服务器a发回来的id会放到服务器a的域之下.不能跨域访问cookie
    使用浏览器随便打开一个网页,然后f12打开.

    可以看到存储在浏览器的cookie

    解决cookie放在本地不安全的问题(session)

    用户在第一次请求之后,浏览器回复的id既可以是用户的user id
    也可以是任意的字符串,我们称之为session id

    根据用户名和密码,服务器会采用自己的规则生成session_id.这个session_id保存在
    本地的cookie里,浏览器请求的时候会携带.

    • 输入用户名&密码
    • 调用login(),后端程序会根据用户名和密码生成session_id,保存在数据库,然后发送给浏览器
    • 用户登录之后,需要通过这个session_id取出这些基本信息

    Django默认的session表中记录了用户登录时,后端我们Django为用户生成的sessionid


    session_key 发送到浏览器取名字为session id
    通过session id用户访问任何一个页面都会携带,服务器会认识

    Setting.py中

    上面的sessions的app会拦截我们的每一次的request请求,在request中找到session id,然后去数据库中进行查询.然后通过session key去找到session data,此时直接为我们取出了user

    在服务器返回浏览器的response中也会直接加上session id

    cookie是浏览器本地存储机制.存在域名之下,存储不安全.
    服务器在返回id时通过规则生成一个字符串,并设置了过期时间.
    存储在服务器端(数据库)

    参考文章

    Django中的cookie和session

    用户注册

    拷贝注册页面进入templates目录
    书写我们要对应处理的view(RegisterView)
    users/views.py

    # 注册视图类
    class Register(View):
        def get(self, request):
            return render(request, 'register.html')
    

    配置对应的url

        url(r'^register/$', RegisterView.as_view(), name='register'),
    

    修改index页面中的注册url

    href = ''{%  url "register" %}'>注册</a>
    

    static静态文件加载参考

    Django中static(静态)文件详解以及{% static %}标签的使用

    关键步骤load staticfile

    <html>的头部位置
    {% load staticfiles %}
    

    使用验证码库实现验证码

    https://github.com/mbi/django-simple-captcha

    安装配置使用第三方的验证码库

    workon mxonline3
    pip install  django-simple-captcha
    workon mxonline2
    pip install  django-simple-captcha==0.4.6
    
    • 将captcha注册到你的INSTALL_APPS下
    • 然后在urls.py中增加一个路由的入口
    rom django.conf.urls import url, include
    urlpatterns += [
        url(r'^captcha/', include('captcha.urls')),
    ]
    
    makemigrations
    migrate
    

    可以看到生成的表

    将验证码展示到页面

    users/forms.py:

    定义我们的register form:

    # 引入验证码field
    from captcha.fields import CaptchaField
    
    # 验证码form & 注册表单form
    class RegisterForm(forms.Form):
        # 此处email与前端name需保持一致。
        email = forms.EmailField(required=True)
        # 密码不能小于5位
        password = forms.CharField(required=True, min_length=5)
        # 应用验证码
        captcha = CaptchaField()
    

    在我们的RegisterView中实例化并传送到前端:

    # form表单验证 & 验证码
    from .forms import LoginForm, RegisterForm
    
    # 注册功能的view
    class RegisterView(View):
        # get方法直接返回页面
        def get(self, request):
            # 添加验证码
            register_form = RegisterForm()
            return render(request, "register.html", {'register_form':register_form})
    

    找到验证码部分

     <img src="[/captcha/image/57fa09852c04cead81c277a36b93a74ce2d629d9/
    (http://127.0.0.1:8000/captcha/image/57fa09852c04cead81c277a36b93a74ce2d629d9/)" alt="captcha" class="captcha" />
    <input id="id_captcha_0" name="captcha_0" type="hidden" value="57fa09852c04cead81c277a36b93a74ce2d629d9" /> 
    <input autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"
     id="id_captcha_1" name="captcha_1" type="text" /> 
    

    我们写的代码只有label,但是前端可以看到input框等,也就是RegisterForm会为我们生成输入框+验证码

    隐藏的字符串的框会被带到后台,由Django为我们进行验证.验证该验证码是否保存过.

    可以看到我们数据库中将这个haskey进行了保存.这个key与验证码内容对应

    后台会把验证码的值与haskey进行联合查询

    编写Register view的后台逻辑

    添加post方法

    def post(self, request):
            # 实例化form
            register_form = RegisterForm(request.POST)
            if register_form.is_valid():
                pass
    

    前端修改form提交的方式以及提交到哪一个url,还有csrf_token

    前端form提交加上对应的crsf token

    刷新验证码是前端帮我们完成的

    //刷新验证码
    function refresh_captcha(event){
        $.get("/captcha/refresh/?"+Math.random(), function(result){
            $('#'+event.data.form_id+' .captcha').attr("src",result.image_url);
            $('#id_captcha_0').attr("value",result.key);
        });
        return false;
    }
    

    获取前端页面值并封装成一个user_profile对象,保存到数据库

    from django.contrib.auth.hashers import make_password
    
            if register_form.is_valid():
                user_name = request.POST.get("email", "")
                pass_word = request.POST.get("password", "")
    
                # 实例化一个user_profile对象,将前台值存入
                user_profile = UserProfile()
                user_profile.username = user_name
                user_profile.email = user_name
    
                # 加密password进行保存
                user_profile.password = make_password(pass_word)
                user_profile.save()
                pass
    

    发送邮件实现

    setting 中配置:

    # 发送Email的服务器,用来在用户注册激活邮箱的时候,给用户发送激活链接.
    EMAIL_HOST = "smtp.sina.com"
    EMAIL_PORT = 25  # 端口号
    EMAIL_HOST_USER = "fioman123@sina.com"  # 用户名
    EMAIL_HOST_PASSWORD = "112233cq"
    EMAIL_USE_TLS = False
    EMAIL_FROM = "fioman123@sina.com"
    

    apps:utils/email_send.py:

    # encoding:utf-8
    from random import Random
    
    from django.core.mail import send_mail # 导入Django自带的Email发送模块
    from users.models import EmailVerifyRecord
    from MxOnline.settings import EMAIL_FROM
    
    
    __author__ = 'Fioman'
    __date__ = '2018/11/23 10:36'
    
    def random_str(random_length=8):
        str = ''
        # 生成字符串的可选字符串
        chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
        length = len(chars) - 1
        random = Random()
        for i in range(random_length):
            str += chars[random.randint(0, length)]
        return str
    
    
    # type 为发送邮箱干嘛的,这里用于激活
    def send_register_email(email,send_ype="register"):
        # 发送之前保存到数据库,以便到时候查询链接是否存在
    
        # 实例化一个EmailVerifyRecord对象
        email_record = EmailVerifyRecord()
        # 生成随机的code放入到链接
        code = random_str(16)
        email_record.code = code
        email_record.email =email
        email_record.send_type = send_ype
    
        # 保存到数据库
        email_record.save()
    
        # 定义邮件的内容
        email_title = ""
        email_body = ""
    
        if send_ype == "register":
            email_title = "智学教育网 注册激活链接"
            email_body = "请点击下面的链接激活你的账号: http://127.0.0.1:8000/active/{0}".format(code)
    
            # 使用Django内置的函数完成邮件发送.四个参数:主题,邮件内容,从哪里发,接受者list
            send_status = send_mail(email_title,email_body,EMAIL_FROM,[email])
    
            # 如果发送成功:
            if send_status:
                pass
    
    
    

    为sina邮箱设置smtp开启服务

    post 中加上发送邮件
    users/views.py:

    修改默认的激活状态为false

    post 方法中

    # 默认激活状态为false
    user_profile_is_active = False
    

    书写处理激活的view

    # 激活用户的view
    class ActiveUserView(View):
        def get(self, request, active_code):
            # 查询邮箱验证记录是否存在
            all_record = EmailVerifyRecord.objects.filter(code = active_code)
            # 激活form负责给激活跳转进来的人加验证码
            active_form = ActiveForm(request.GET)
            # 如果不为空也就是有用户
            if all_record:
                for record in all_record:
                    # 获取到对应的邮箱
                    email = record.email
                    # 查找到邮箱对应的user
                    user = UserProfile.objects.get(email=email)
                    user.is_active = True
                    user.save()
                    # 激活成功跳转到登录页面
                    return render(request, "login.html", )
            # 自己瞎输的验证码
            else:
                return render(request, "register.html", {"msg": "您的激活链接无效","active_form": active_form})
    

    配置用户激活的url并且通过url提取到变量

    # 激活用户url
        url(r'^active/(?P<active_code>.*)/$',ActiveUserView.as_view(), name= "user_active")
    

    这里通过?p将后面.*代表全部提取的正则,符合的内容传入参数active_code中/代表以/为结尾

    注册功能制作完毕,流程:注册,发送邮件,激活,登录.

    密码找回功能的实现

    1. 书写处理忘记密码的view,并且配置对应的url

    # 忘记密码的view,点击忘记密码的时候,会调用这个视图
    class ForgetView(View):
        def get(self,request):
            return render(request,"forgetpwd.html",{})
    

    配置url

    url(r'^forget/$', ForgetView.as_view(), name='forget_pwd'),
    

    2.定义form,用来生成验证码

    users/forms.py:

    # 忘记密码的表单,用来生成验证码
    class ForgetFrom(forms.Form):
        email = forms.CharField(required=True)
        captcha = CaptchaField(error_messages={'invalid': '验证码错误'})
    

    3.发送邮件的逻辑,当发送的类型是'forget'的时候,邮件发送的内容

    # type 为发送邮箱干嘛的,这里用于激活
    def send_register_email(email, send_type="register"):
        # 发送之前保存到数据库,以便到时候查询链接是否存在
    
        # 实例化一个EmailVerifyRecord对象
        email_record = EmailVerifyRecord()
        # 生成随机的code放入到链接
        code = random_str(16)
        email_record.code = code
        email_record.email =email
        email_record.send_type = send_type
    
        # 保存到数据库
        email_record.save()
    
        # 定义邮件的内容
        email_title = ""
        email_body = ""
    
        if send_type == "register":
            email_title = "智学教育网 注册激活链接"
            email_body = "请点击下面的链接激活你的账号: http://127.0.0.1:8000/active/{0}".format(code)
    
            # 使用Django内置的函数完成邮件发送.四个参数:主题,邮件内容,从哪里发,接受者list
            send_status = send_mail(email_title,email_body,EMAIL_FROM,[email])
    
            # 如果发送成功:
            if send_status:
                pass
    
        elif send_type == 'forget':
            email_title = "智学教育网 密码重置连接"
            email_body = "请点击下面的链接激活你的账号: http://127.0.0.1:8000/reset/{0}".format(code)
    
            # 使用Django内置的函数完成邮件发送.四个参数:主题,邮件内容,从哪里发,接受者list
            send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
            # 如果发送成功:
            if send_status:
                pass
    
    

    4.点击这个链接的时候,验证显示密码修改的页面

    urls.py

        url(r'^reset/(?P<active_code>.*)/$', ResetView.as_view(), name='reset_pwd'),
    

    views.py

    # 重置密码的视图处理类
    class ResetView(View):
        def get(self, request, active_code):
            # 查询邮箱验证记录是否存在
            all_record = EmailVerifyRecord.objects.filter(code=active_code)
            # 进行表单验证
            active_form = ActiveForm(request.GET)
    
            if all_record:
                for record in all_record:
                    # 获取对应的邮箱
                    email = record.email
                    # 将email传回来
                    return render(request, 'password_reset.html', {'email': email})
            else:
                return render(request, 'forgetpwd.html', {"msg": '你重置的密码连接无效,请重新请求', "active_form": active_form})
    

    因为重置密码的页面需要传入参数,但是提交的时候不需要参数,所有这个get和提交不能在同一个url配置里面.

    创建改变密码的forms

    # 重置密码form实现
    class ModifyPwdForm(forms.Form):
        # 密码不能小于6位
        password1 = forms.CharField(required=True)
        password2 = forms.CharField(required=True)
    

    书写改变密码的view.这个view主要处理判断密码是否可用以及将新密码保存到数据库

    
     处理重置密码提交表单的View的视图类
    class ModifyPwdView(View):
        def post(self, request):
            modifypwd_form = ModifyPwdForm(request.POST)
            if modifypwd_form.is_valid():
                pw1 = request.POST.get("password1", "")
                pw2 = request.POST.get("password2", "")
                email = request.POST.get("email", "")
                # 如果两次的密码不一样,返回错误信息
                if pw1 != pw2:
                    return render(request, 'password_reset.html', {"email": email, "msg": '密码不一致'})
                # 如果密码一致
                user = UserProfile.objects.get(email=email)
                # 加密成密文,数据库存储的是密文
                user.password = make_password(pw1)
                # save保存到数据库
                user.save()
                # 返回到登录页面提示,密码修改成功
                return render(request, 'login.html', {'msg': '密码修改成功,请登录'})
            # 验证失败说明密码位数不够
            else:
                email = request.POST.get("email", "")
                return render(request, 'password_reset.html', {"email": email, "modifypwd_form": modifypwd_form})
    
    

    配置modify的url

        url(r'^modify_pwd/$', ModifyPwdView.as_view(), name='modify_pwd'),
    
    

    更多扩展:

    重置密码链接是否被点击过,过期时间等

    相关文章

      网友评论

          本文标题:注册登录首页展示实现

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