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

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

1 注册功能

1.1 注册功能form组件

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

<!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 注册功能后端

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组件和前后端总结

# 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 登录前端

<!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 生成验证码

# 第三方方案
https://pythonjishu.com/ljpdvvedzkqiovs/

# 自己的方案

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())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值