【bbs02补】注册功能form组件-前端-后端-总结、登录功能(前端、后端、生成验证码)

[1 注册功能](#1 注册功能)
[1.1 注册功能form组件](#1.1 注册功能form组件)
[1.2 注册功能前端](#1.2 注册功能前端)
[1.3 注册功能后端](#1.3 注册功能后端)
[1.4 forms组件和前后端总结](#1.4 forms组件和前后端总结)
[2 登录功能](#2 登录功能)
[2.1 登录前端](#2.1 登录前端)
[2.2 生成验证码](#2.2 生成验证码)

1 注册功能

1.1 注册功能form组件

python 复制代码
# 注册页面
	-用户名
    -密码
    -确认密码
    -邮箱
    -手机号
    -头像
    
    
# form组件 可以帮助我们
	1 快速生成前端页面
    2 数据校验
    3 错误处理
    
    
# 如何使用
	-1 写一个类,继承forms.Form
    -2 在类中写属性和方法
    	-属性:要跟咱们要校验或自动生成页面的字段一一对应
        -方法:对字段进行校验:
        	clean_字段名 给单个字段校验
            clean 给多个字段校验
    -3 在视图函数中使用
    -4 模板中使用
    
    
    
#  form表单中,如果定义了button或input 类型是"submit",只要点击,就会默认触发form表单的提交,如果我们又写了ajax提交,就会触发两次---》导致问题-
	-把它搞外面
    -input 类型是"button"
python 复制代码
from django import forms
from django.forms import widgets, ValidationError
from .models import UserInfo


class RegisterForm(forms.Form):
    # max_length=18 最长 18
    # min_length=3 最短 3
    # required=True 必填
    username = forms.CharField(max_length=18, min_length=3, required=True,
                               label='用户名',
                               error_messages={
                                   'required': '用户名字段必填',
                                   'max_length': '长度不能超过18',
                                   'min_length': '最短3'
                               }, widget=widgets.TextInput(attrs={'class': 'form-control'}))

    password = forms.CharField(max_length=18, min_length=3, required=True,
                               label='密码',
                               error_messages={
                                   'required': '用户名字段必填',
                                   'max_length': '长度不能超过18',
                                   'min_length': '最短3'
                               }, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))

    re_password = forms.CharField(max_length=18, min_length=3, required=True,
                                  label='确认密码',
                                  error_messages={
                                      'required': '用户名字段必填',
                                      'max_length': '长度不能超过18',
                                      'min_length': '最短3'
                                  }, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(max_length=18, min_length=3, required=True,
                             label='邮箱',
                             error_messages={
                                 'required': '用户名字段必填',
                                 'max_length': '长度不能超过18',
                                 'min_length': '最短3'
                             }, widget=widgets.EmailInput(attrs={'class': 'form-control'}))
    phone = forms.CharField(max_length=11, min_length=11, required=True,
                            label='手机号',
                            error_messages={
                                'required': '用户名字段必填',
                                'max_length': '长度不能超过11',
                                'min_length': '必须11为'
                            }, widget=widgets.TextInput(attrs={'class': 'form-control'}))

    # 方法名只能写两类
    # 一类是  clean_字段名  校验单个字段
    def clean_username(self):  # 如果能走到这里,说明上面的校验已经通过了,校验过后的数据都放在一个字典中---》cleaned_data
        username = self.cleaned_data.get('username')
        # 用户名不能以sb开头
        if username.startswith('sb'):
            # 校验不通过,抛异常
            raise ValidationError('名字不能以sb开头')
        # 如果用户名存在,也不能注册了
        res = UserInfo.objects.filter(username=username).exists()
        if res:
            raise ValidationError('该用户已经存在')
        return username

    # 二类是  clean   同时校验多个字段
    def clean(self):
        password = self.cleaned_data.get('password')
        re_password = self.cleaned_data.get('re_password')
        if not password == re_password:
            raise ValidationError('两次密码不一致')
        return self.cleaned_data

1.2 注册功能前端

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/js/jquery.min.js"></script>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
</head>
<body>

<div class="container-fluid">


    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center">注册功能</h1>
            <form id="register_form">
                {% csrf_token %}
                {% for foo in form %}
                    <div class="form-group">
                        <label for="{{ foo.auto_id }}">{{ foo.label }}</label>
                        {{ foo }} <span class="pull-right error" style="color: red"></span>
                    </div>
                {% endfor %}
                <div class="form-group">
                    <label for="id_avatar">头像
                        <img src="/static/img/default.png" alt="" height="80px" width="80px" id="id_img"
                             style="margin-left: 20px">

                    </label>
                    <input type="file" id="id_avatar" class="form-control" accept="image/*" style="display: none">
                </div>
                <div class="text-center">
                    <input type="button" value="注册" class="btn btn-danger" id="id_submit">
                    <span class="error" style="color: darkred;margin-left: 10px" id="id_error"></span>
                </div>


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

</body>

<script>
    // 1 监控文件变化
    $('#id_avatar').change(function () {
        // 读出input 的图片,写到 img标签上
        // 需要借助于文件阅读器
        var fileReader = new FileReader();
        // 把文件对象读入文件阅读器中
        fileReader.readAsDataURL($('#id_avatar')[0].files[0])
        // 等文件读完,再放入
        fileReader.onload = function () {
            //$('#id_img').attr('height', '300px')
            $('#id_img').attr('src', fileReader.result)
            //$('#id_img')[0].src = fileReader.result
        }


    })


    // 2 按钮提交---》注册功能
    $('#id_submit').click(function () {

        var formdata = new FormData()
        // 把文件放入
        formdata.append('my_img', $('#id_avatar')[0].files[0])
        //放数据:用户名,密码,确认密码,手机号,邮箱 ,你可以一个个放--->笨办法
        /*
        formdata.append('username', $('#id_username').val())
        formdata.append('password', $('#id_password').val())
        formdata.append('re_password', $('#id_re_password').val())
        formdata.append('phone', $('#id_phone').val())
        formdata.append('email', $('#id_email').val())
        formdata.append('csrfmiddlewaretoken', '{{ csrf_token }}') // csrf 的token
        */

        // 简单方案
        var register_form = $('#register_form').serializeArray() // 会把当前form表单中得数据放到列表套字典的形式
        /*
        数组
        [{name:xx,value:yy}, {...}, {...}, {...}, {...}]
        */
        //console.log(register_form)
        // jq 的循环
        $.each(register_form, function (i, v) {
            //console.log(v['name'])
            //console.log(v['value'])
            formdata.append(v['name'], v['value'])
        })

        $.ajax({
            url: '/register/',
            method: 'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data) {
                if (data.code == 100) {
                    location.href = '/login/'
                } else { // 不成功
                    // 两次密码不一致,把错误写在 注册按钮后面
                    // input 自己的错误,写在自己后面
                    // 循环返回的错误
                    $.each(data.errors, function (key, value) {
                        if (key == '__all__') {
                            $('#id_error').html(value[0])
                        }
                        $('#id_' + key).next().html(value[0]).parent().addClass('has-error')

                    })


                    // 过3s后,清空错误,和红框
                    setTimeout(function () {
                        $('.error').html("").parent().removeClass('has-error')
                        //alert('asfdsdaf')
                    }, 3000)


                }
            }
        })


    })

    // 3 当用户名输入框失去焦点,我们就去后端校验用户名是否注册过
    $('#id_username').blur(function () {
        //alert('失去焦点了')
        //var username=$('#id_username').val()
        var username = $(this).val()
        $.ajax({
            url: '/check_username/?username=' + username,
            method: 'get',
            success: function (data) {
                console.log(data)
                if (data.code != 100) {
                    // 1 清空输入框
                    //$(this).val()
                    // 2 错误提示
                    //$(this).next().html(data.msg)
                    console.log('ssss')
                    // 两句可以并做一句---》链式调用
                    // 如果在另一个内部函数中,就不能用this
                    //var ss=$('#id_username').val()
                    //$('#id_username').next().html(data.msg).parent().addClass('has-error').children('input').val("")
                    $('#id_username').val('').next().html(data.msg).parent().addClass('has-error')

                }
            }
        })
    })

</script>
</html>

1.3 注册功能后端

python 复制代码
from django.shortcuts import render
from .forms import RegisterForm
from .models import UserInfo
from django.http import JsonResponse


def register(request):
    if request.method == 'GET':
        form = RegisterForm()
        return render(request, 'register.html', {'form': form})
    else:
        # # 1 数据
        # print(request.POST)
        # # 2 文件
        # print(request.FILES.get('my_img'))
        # 取出头像
        avatar = request.FILES.get('my_img')
        # 校验数据是否合法
        '''
        username: admin
        password: 123
        email: an@qq.com
        phone: 12345678935
        avatar:文件
        '''
        form = RegisterForm(request.POST)  # 使用form校验传入的数据
        if form.is_valid():  # 校验通过
            # 保存
            data = form.cleaned_data
            # 把re_password 弹出
            data.pop('re_password')
            # 把头像加入
            if avatar:
                data['avatar'] = avatar
            UserInfo.objects.create_user(**data)
            return JsonResponse({'code': 100, 'msg': '注册成功'})
        else:
            return JsonResponse({'code': 101, 'msg': '注册失败', 'errors': form.errors})


# 校验用户名是否存在的接口
def check_username(request):
    username = request.GET.get('username')
    res = UserInfo.objects.filter(username=username).exists()
    if res:
        # 约定状态码:100,表示成功,非100表示失败
        return JsonResponse({'code': 101, 'msg': '用户已经存在'})
    return JsonResponse({'code': 100, 'msg': '您可以注册'})

1.4 forms组件和前后端总结

python 复制代码
# 1 forms组件
	-1 渲染模板
    -2 校验数据
    -3 渲染错误
    - form=RegisterForm()---渲染页面
    - form=RegisterForm(requets.POST)---校验数据
# 2 取数据,取文件
	-form-data提交数据
    -request.POST             中取数据
    -request.FILES.get('名字') 取文件
    -补:前端是key:value    后端变成了  key:[value]
    	-request.data 不是真正的字典
     	{'username': ['admin','xxx'], 'password': ['123']}
        request.POST.get('username')
        request.POST.getlist('username')
        
# 3 保存 文件和数据
	# data 是form校验过后的数据,没有头像
	data.pop('re_password') # 不是表的字段
    # 把头像加入 头像是表的字段
    if avatar:
        data['avatar'] = avatar # 文件对象
   # avatar = models.ImageField(upload_to='avatar', default='avatar/default.png')
   内部自动:打开一个空文件,把文件写入到空文件中 【/media/avatar/】,并且把路径赋值给avatar数据库字段
   UserInfo.objects.create_user(**data)


# 4 前端:头像实时显示
	-隐藏了 input  file---》input只能接收图片类型
    -只要input发生变化(change)---》把图片读出来,写入到 img标签中
    	var fileReader = new FileReader();
        fileReader.readAsDataURL($('#id_avatar')[0].files[0])
        fileReader.onload = function () {
            $('#id_img').attr('src', fileReader.result)
        }
        
        
        
# 5 用户名失去焦点(blur)---》向后端校验

# 6 form表单使用var register_form = $('#register_form').serializeArray() 转到数组中

# 7 $.each(可以被循环的,function(){})

# 8 错误信息渲染
	-__all__ 全局错误---》显示在注册后面
         if (key == '__all__') {
           $('#id_error').html(value[0])
          }
    -其他错误,显示在自己后面
    	$('#id_' + key).next().html(value[0]).parent().addClass('has-error')
        
    -定时任务:3s后干什么
       setTimeout(function () {
             $('.error').html("").parent().removeClass('has-error')
         }, 3000)

2 登录功能

2.1 登录前端

python 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/js/jquery.min.js"></script>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
</head>
<body>

<div class="container-fluid">


    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center">登录功能</h1>
            <form>
                <div class="form-group">
                    <label for="">用户名</label>
                    <input type="text" name="username" class="form-control">
                </div>
                <div class="form-group">
                    <label for="">密码</label>
                    <input type="text" name="password" class="form-control">
                </div>
                <div class="form-group">
                    <label for="">验证码</label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" name="code" class="form-control">
                        </div>

                        <img src="/get_code/" alt="" class="col-md-6" height="35">
                    </div>
                </div>
                <div class="text-center" style="margin-top: 50px">
                    <input type="button" value="登录" class="btn btn-danger" id="id_submit">
                    <span class="error" style="color: darkred;margin-left: 10px" id="id_error"></span>
                </div>
            </form>
        </div>
    </div>
</div>

</body>

</html>

2.2 生成验证码

python 复制代码
# 第三方方案
https://pythonjishu.com/ljpdvvedzkqiovs/

# 自己的方案
python 复制代码
def get_code(request):
    # 前端显示图片 方式一
    # with open('./static/img/4.jpg', 'rb') as f:
    #     data = f.read()
    # return HttpResponse(data)

    # 方式二:自己生成一张图片,保存到本地-->打开,返回给前端
    # image_tmp = Image.new('RGB', (300, 38), (255, 255, 0))
    #
    # with open('code.png', 'wb') as f:
    #     image_tmp.save(f, 'png')
    #
    # with open('code.png', 'rb') as f:
    #     data = f.read()
    # return HttpResponse(data)

    # 方式三:借助于ByteIo,把文件内容放在内存中
    # image_tmp = Image.new('RGB', (300, 38), (0, 255, 0))
    # # 放在内存中
    # my_io = BytesIO()
    # image_tmp.save(my_io, 'png')
    # return HttpResponse(my_io.getvalue())

    # 方式四: 要在图片上写文字
    # image_tmp = Image.new('RGB', (300, 38), (0, 255, 0))
    # # 把空图片放在了画板上
    # draw = ImageDraw.Draw(image_tmp)
    # draw.text((0, 0), 'lqz')
    # my_io = BytesIO()
    # image_tmp.save(my_io, 'png')
    # return HttpResponse(my_io.getvalue())

    # 方式5 :加入字体文件
    # image_tmp = Image.new('RGB', (300, 38), (0, 255, 0))
    # # 把空图片放在了画板上
    # draw = ImageDraw.Draw(image_tmp)
    # # 加入字体
    # img_font = ImageFont.truetype('./static/font/xgdl.ttf', 23)
    # draw.text((0, 0), '西瓜大朗', fill=(0, 0, 128), font=img_font, )
    # my_io = BytesIO()
    # image_tmp.save(my_io, 'png')
    # return HttpResponse(my_io.getvalue())

    # 方式6 ,随机生成 5 大小写字母和数字,图片背景色和字的颜色每次不一样
    image_tmp = Image.new('RGB', (300, 38), (0, 255, 0))
    # 把空图片放在了画板上
    draw = ImageDraw.Draw(image_tmp)
    # 加入字体
    img_font = ImageFont.truetype('./static/font/xgdl.ttf', 23)
    draw.text((0, 0), '西瓜大朗', fill=(0, 0, 128), font=img_font, )
    my_io = BytesIO()
    image_tmp.save(my_io, 'png')
    return HttpResponse(my_io.getvalue())




import random


def get_random_code():
    code = ''
    for i in range(5):
        # 随机生成一个大写字母
        upper_char = chr(random.randint(65, 90))
        low_char = chr(random.randint(97, 122))
        num_char = str(random.randint(0, 9))
        res = random.choice([upper_char, low_char, num_char])
        code += res
    return code


if __name__ == '__main__':
    print(get_random_code())
相关推荐
qq_2290580114 小时前
运行djando项目 配置启动类 label_studio包含前后端启动方法
python·django
码界奇点17 小时前
基于Python与Django的白泽自动化运维系统设计与实现
运维·python·django·毕业设计·源代码管理
计算机毕业编程指导师17 小时前
【Python大数据选题】基于Spark+Django的电影评分人气数据可视化分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·计算机·spark·django·电影评分人气
Python极客之家18 小时前
基于Django的高校二手市场与社交系统
后端·python·数据挖掘·django·毕业设计
码界奇点19 小时前
基于Django与Zabbix集成的运维故障管理系统设计与实现
运维·django·毕业设计·zabbix·源代码管理
码界奇点19 小时前
基于Django与Ansible的自动化运维管理系统设计与实现
运维·python·django·毕业设计·ansible·源代码管理
U盘失踪了19 小时前
Django 学生成绩管理系统
django
倔强青铜三3 天前
Django 6.0来袭!这些新特性,真的令人振奋!
人工智能·python·django
Java水解4 天前
Django实现接口token检测的实现方案
后端·django
飞Link4 天前
【Django】Django 调用外部 Python 程序的完整指南
后端·python·django·sqlite