注册登录首页展示实现

2018-11-27  本文已影响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>

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里,浏览器请求的时候会携带.

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
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'),

更多扩展:

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

上一篇下一篇

猜你喜欢

热点阅读