Django框架 用户登录和注册

备注:用户的登录、注册

models.py

Django默认的认证系统auth_user字段是固定的,如果想存储用户的联系方式,就需要通过继承内置的AbstractUser类,来定义一个Model类。

在settings.py中告诉Django,现在使用新定义的UserInfo表来做用户认证

from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    phone = models.CharField(max_length=11,verbose_name='手机号')
    avatar = models.FileField(upload_to='avatar/',default='avatar/avatar.png')

settings.py

LANGUAGE_CODE = 'zh-hans'    #页面显示中文

STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'static')
]

#使用自建的UserInfo表
AUTH_USER_MODEL = 'fault_reporting.UserInfo'

登录

路由 urls.py

from fault_reporting import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/',views.LoginView.as_view()),
    url(r'^register/',views.RegisterView.as_view()),
    url(r'^index/',views.index),
]

视图 views.py

auth模块:

authenticate()  提供用户认证功能,需要username和password参数

login()   该函数接收一个HttpRequest对象,以及一个经过认证的user对象。实现用户登录功能,本质上会在后端为用户生成相关session数据

from django.shortcuts import render,HttpResponse,redirect
from django.contrib import auth


# 登录
class LoginView(views.View):
    def get(self,request):
        return render(request,'login.html')

    def post(self,request):
        username = request.POST.get("username")
        pwd = request.POST.get("password")

        user_obj = auth.authenticate(username=username,password=pwd)
        if user_obj:
            auth.login(request,user_obj)
        return redirect('/index/')
from django.shortcuts import render,HttpResponse,redirect
from django import views
from django.contrib import auth


# 登录
class LoginView(views.View):
    def get(self,request):
        return render(request,'login.html')

    def post(self,request):
        next_url = request.GET.get("next",'/index/')
        username = request.POST.get("username")
        pwd = request.POST.get("password")
        vcode = request.POST.get("vcode",'').upper()
        # 判断验证码是否正确
        if vcode == request.session.get("v_code"):
            user_obj = auth.authenticate(username=username,password=pwd)
            if user_obj:
                auth.login(request,user_obj)
                return redirect('/index/')
            else:
                return render(request,'login.html',{"error_msg":"用户名或密码错误"})
        else:
            return render(request,'login.html',{"error_msg":"验证码错误"})
带验证码功能
from django.shortcuts import render,HttpResponse,redirect
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from fault_reporting import models
import random
import re

# 专门返回验证码图片的视图
def vcode(request):
    from PIL import Image,ImageDraw,ImageFont
    def vcode_col():
        return random.randint(0,255),random.randint(0,255),random.randint(0,255)
    # 创建一个图片对象
    image_obj = Image.new(
        'RGB',
        (250,35),
        (192,192,192)
    )
    # 在该图片对象上生成一个画笔对象
    draw_obj = ImageDraw.Draw(image_obj)
    # 加载一个字体对象
    font_obj = ImageFont.truetype('static/font/kumo.ttf',34)
    # 生成验证码
    tmp = []
    for i in range(5):
        l = chr(random.randint(97,122))
        u = chr(random.randint(65,90))
        n = str(random.randint(0,9))
        r = random.choice([l,u,n])
        # 将选中的字符写在图片上
        # draw_obj.text((40*i+20,0),r,fill=vcode_col(),font=font_obj)
        draw_obj.text((40*i+20,0),r,fill=(0,0,0),font=font_obj)
        tmp.append(r)
    v_code = "".join(tmp).upper()
    # 将生成的验证码保存
    request.session["v_code"] = v_code

    #直接在内存中保存图片,替代io操作
    from io import BytesIO
    f1 = BytesIO()
    image_obj.save(f1,format="PNG")
    img_data = f1.getvalue()

    return HttpResponse(img_data,content_type="image/png")
显示验证码视图

 

login.html

登录页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>

<div class="container">
    <div class="row">
        <div class="md-col-6 md-col-offset-3">
            <form action="" method="post">
                {% csrf_token %}

                <div class="form-group">
                    <label for="id_username">用户名</label>
                    <input type="text" class="form-control" id="id_username" name="username" placeholder="用户名">
                </div>

                <div class="form-group">
                    <label for="id_password">密码</label>
                    <input type="password" class="form-control" id="id_password" name="password" placeholder="密码">
                </div>

                <button type="submit" class="btn-info btn" id="login-btn">登录</button>
                <a href="/register/" class="btn btn-info" id="register-btn">注册</a>
            </form>
        </div>
    </div>
</div>


</body>
</html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>

<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <form action="" method="post">
                {% csrf_token %}

                <div class="form-group">
                    <label for="id_username">用户名</label>
                    <input type="text" class="form-control" id="id_username" name="username" placeholder="用户名">
                </div>

                <div class="form-group">
                    <label for="id_password">密码</label>
                    <input type="password" class="form-control" id="id_password" name="password" placeholder="密码">
                </div>

                <div class="form-group">
                    <label for="id_vcode" style="display: block">验证码</label>
                    <input type="text" class="form-control" id="id_vcode" name="vcode" placeholder="验证码"
                           style="width: 250px;display: inline-block">
                    <img src="/vcode/" style="width: 250px;height: 35px; display: inline-block; float: right">
                </div>
                <p style="color: red ; font-size: 14px">{{ error_msg }}</p>

                <button type="submit" class="btn-info btn" id="login-btn">登录</button>
                <a href="/register/" class="btn btn-info" id="register-btn">注册</a>
            </form>
        </div>
    </div>
</div>
</body>
带验证码功能html

注销功能

# urls.py

url(r'^logout/$',views.logout),


# views.py

def logout(request):
    auth.logout(request)
    return redirect('/login/')

注册

forms.py

手动创建一个form 类,

from django import forms
from django.core.validators import RegexValidator   # 正则
from django.core.exceptions import ValidationError  # 抛出异常
from fault_reporting import models
import re

# 自定义正则函数
def check_email(value):
    ret = re.match(r'^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$', value)
    if not ret:
        raise ValidationError("邮箱格式不正确")
    else:
        return value


# 注册的form类
class RegisterForm(forms.Form):
    username = forms.CharField(max_length=12,label='用户名')
    password = forms.CharField(
        label='密码',
        min_length=6,
        widget=forms.widgets.PasswordInput()
    )
    re_password = forms.CharField(
        label='确认密码',
        min_length=6,
        widget=forms.widgets.PasswordInput()
    )
    phone = forms.CharField(
        label='手机号',
        min_length=11,
        max_length=11,
        validators=[RegexValidator(r'^1[3-9]\d{9}$',"手机号码格式不正确")]
    )
    email = forms.CharField(
        label='邮箱',
        validators=[check_email,]
    )

    # 局部钩子,判断用户名是否被注册
    def clean_username(self):
        # 做用户名不能重复的校验
        username = self.cleaned_data.get("username")
        # 去数据库查重
        is_exist = models.UserInfo.objects.filter(username=username)
        if is_exist:
            raise ValidationError("该用户已被注册")
        else:
            return username

    # 全局钩子,将多个字段进行比较
    def clean(self):
        password = self.cleaned_data.get("password")
        re_password = self.cleaned_data.get("re_password")
        if password != re_password:
            self.add_error("re_password","密码不一致")
            raise ValidationError("密码不一致")


    def __init__(self,*args,**kwargs):
        super(RegisterForm,self).__init__(*args,**kwargs)
        # 循环给每个字段加 class: form-control
        for field in iter(self.fields):
            self.fields[field].widget.attrs.update({
                'class': 'form-control'
            })

views.py

from django.shortcuts import render,HttpResponse,redirect
from fault_reporting import models
from fault_reporting import forms
from django.contrib import auth
from django.http import JsonResponse
from django import views

# 注册功能
class RegisterView(views.View):
    def get(self,request):
        # 实例化一个form对象
        form_obj = forms.RegisterForm()
        return render(request,'register.html',locals())

    def post(self,request):
        # 创建一个空字典,用于判断当数据有问题时的情况
        res = {"code":0}
        # 给用户提交过来的数据做有效性校验
        form_obj = forms.RegisterForm(request.POST)
        # 调用 is_valid() 方法,就是替我们做数据有效性校验
        if form_obj.is_valid():
            # 数据没有问题
            # 去数据库创建一条记录
            # 所有经过校验的数据,forms里没有头像字段
            # 创建用户
            # 从form表单中把 re_password  数据移除,因为UserInfo表不需要该字段
            form_obj.cleaned_data.pop("re_password")
            # 头像数据
            avatar_obj = request.FILES.get("avatar")
            # models.py 中avatar对应的是FileField,
            models.UserInfo.objects.create_user(**form_obj.cleaned_data, avatar=avatar_obj)
            res["url"] = '/login/'
        else:
            res["code"] = 1
            res["error"] = form_obj.errors
        #  res 传送到 register.html 中
        return JsonResponse(res)

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-4 col-md-offset-4">

            <form action="" class="register-form">
                {% csrf_token %}
                {% for field in form_obj %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {{ field }}
                        <span class="help-block"></span>
                    </div>
                {% endfor %}

                <div class="form-group">
                    <label for="id_avatar" >头像
                        <img src="/static/images/default.jpg" id="avatar-img" style="width: 60px; height: 60px">
                    </label>
                    <!-- accept="image/*" 默认只能选择图片格式 -->
                    <input type="file" id="id_avatar" accept="image/*" class="form-control" style="display: none">

                </div>
                <!-- 使用ajax提交数据时,不要写成 submit类型-->
                <!-- <button type="submit" class="btn btn-info">注册</button> -->
                <button type="button" class="btn btn-info" id="submit-btn">注册</button>

            </form>
        </div>
    </div>
</div>

<script src="/static/jquery-3.3.1.min.js"></script>
<script>
    $("#submit-btn").click(function () {
{#        var username = $("#id_username").val();#}
{#        var password = $("#id_password").val();#}
{#        var re_password = $("#id_re_password").val();#}
{#        var phone = $("#id_phone").val();#}
{#        var email = $("#id_email").val();#}
{#        var csrfToken = $("[name='csrfmiddlewaretoken']").val();#}
        // 因为注册功能有头像文件,所以用FormData对象提交数据
        var fd = new FormData();
        fd.append("username",$("#id_username").val());
        fd.append("password",$("#id_password").val());
        fd.append("re_password",$("#id_re_password").val());
        fd.append("phone",$("#id_phone").val());
        fd.append("email",$("#id_email").val());
        fd.append("csrfmiddlewaretoken",$("[name='csrfmiddlewaretoken']").val());
        // avatar 头像
        fd.append("avatar",$("#id_avatar")[0].files[0]);

        $.ajax({
            url: '/register/',
            type: 'post',
            contentType: false,     // 不让jQuery去设置提交数据的类型
            processData: false,     // 不让jQuery去处理提交的数据
            data: fd,
            success:function (res) {
                console.log(res);
                if (res["code"] === 1){
                    // 说明有字段错误,使用each 循环遍历出来 。
                    // $.each('要遍历的对象', function(k, v))
                    $.each(res.error , function (k, v) {
                        console.log(k , v[0]);
                        $("#id_" + k).next().text(v[0]).parent().addClass("has-error");

                    })
                }else {
                    // 没有错误,默认跳转到登录界面
                    location.href = res.url
                }
            }
        });
    });
    // 给input 标签绑定获取标点就删除错误提示的动作
    $(".register-form input").focus(function () {
        $(this).next().text("").parent().removeClass("has-error");
    });

    // 头像预览功能
    $("#id_avatar").change(function () {
        // 取到用户选中的头像文件
        var fileObj = this.files[0];
        // 新建一个FileReader 对象,从本地磁盘加载文件数据
        var fr = new FileReader();
        fr.readAsDataURL(fileObj);
        // 读取文件
        fr.onload = function () {
            // 找到头像预览的 img 标签,把它的src 属性设置成读取的用户选中的图片
            $("#avatar-img").attr("src",fr.result)
        };
    });

</script>
</body>
</html>
注册+头像预览功能

settings.py

# 所有用户上传的文件
# 告诉Django项目将用户上传的文件保存在服务端那个目录下
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# 告诉Django 用户通过什么前缀来访问刚才定义的目录
MEDIA_URL = '/media/'

urls.py

url(r'^register/',views.RegisterView.as_view()),

url(r'^vcode/',views.vcode),

# 给用户上传的文件做一个对应关系
url(r'^media/(?P<path>.*)',serve,{"document_root":settings.MEDIA_ROOT}),

 

posted @ 2018-09-04 15:48  叫你你敢答应么  阅读(201)  评论(0)    收藏  举报