django1实现 表结构设计准备、注册、 登录功能之图片验证码推导操作

一、BBS项目

路由

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^register/', views.register, name='reg'),
    url(r'^login/', views.login, name='login'),
    # 图片验证码相关操作
    url(r'^get_code/', views.get_code, name='gc'),
]

表结构设计准备

from django.db import models

from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    phone = models.BigIntegerField(verbose_name='手机号', null=True)
    # 头像
    avatar = models.FileField(upload_to='avatar', default='avatar/default.png', verbose_name='用户头像')
    '''
    给avatar字段文件对象,该文件自动存储到avatar文件下,然后该字段只会保存在
    '''
    create_time = models.DateField(auto_now_add=True)

    blog = models.OneToOneField(to='Blog', null=True)


class Blog(models.Model):
    site_name = models.CharField(verbose_name='站点名称', max_length=32)
    site_title = models.CharField(verbose_name='站点标题', max_length=32)
    # 存储css/js文件路径
    site_theme = models.CharField(verbose_name='站点样式', max_length=64)


class Category(models.Model):
    name = models.CharField(validators='文章分类', max_length=32)
    blog = models.ForeignKey(to='Blog', null=True)


class Tag(models.Model):
    name = models.CharField(verbose_name='文章标签', max_length=32)
    blog = models.ForeignKey(to='Blog', null=True)


class Article(models.Model):
    title = models.CharField(verbose_name='文章标题', max_length=64)
    desc = models.CharField(verbose_name='文章简介', max_length=255)
    # 文章内容很多,一般使用TestFiled
    content = models.TextField(verbose_name='文章内容')
    create_time = models.DateField(auto_now_add=True)

    # 数据库字段设计优化
    up_num = models.BigIntegerField(verbose_name='点赞数', default=0)
    down_num = models.BigIntegerField(verbose_name='点踩数', default=0)
    comment_num = models.BigIntegerField(verbose_name='评论数', default=0)

    # 外键字段
    blog = models.ForeignKey(to='Blog', null=True)
    category = models.ForeignKey(to='Category', null=True)
    tags = models.ManyToManyField(to='Tag',
                                  through='Article2Tag',
                                  through_fields=('article', 'tag')
                                  )


# 文章和标签是多对多,创建第三张关系表,半自动
class Article2Tag(models.Model):
    article = models.ForeignKey(to='Article')
    tag = models.ForeignKey(to='Tag')


class UpAndDown(models.Model):
    user = models.ForeignKey(to='UserInfo')
    article = models.ForeignKey(to='Article')
    is_Up = models.BooleanField() # 传布尔值 存0/1


class Comment(models.Model):
    user = models.ForeignKey(to='UserInfo')
    article = models.ForeignKey(to='Article')
    content = models.CharField(verbose_name='评论内容', max_length=255)
    comment_time = models.DateTimeField(auto_now_add=True)
    # 自关联   在这里必须加一个null=True 有些评论是根评论,有些事子评论
    parent = models.ForeignKey(to='self', null=True)

二、注册功能

1.前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    {% load static %}
    <script src="{% static 'js/jquery.min.js' %}"></script>
    <link href="{% static 'bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
    <script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
    <script src="{% static 'layer/layer.js' %}"></script>

</head>
<body>

<div class="container-fluid">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h1 class="text-center">注册</h1>
            <!-- 不用form表单提交,只是单纯的用一下而已 -->
            <form id="myform">
                {% csrf_token %}
                {% for form in form_obj %}
                    <div class="form-group">
                        <label for="{{ form.auto_id }}">{{ form.label }}</label>
                        {{ form }}
                        <span style="color:red;" class="pull-right" ></span>
                    </div>
                {% endfor %}
                <div class="form-group">
                    <label for="myfile">头像
                        {% load static %}
                        <img src="{% static 'img/default.png' %}" id="myimg" width="80" alt=""
                             style="margin-left: 10px;">
                    </label>
                    <input type="file" id="myfile" name="avatar" style="display: none">
                </div>

                <input type="button" value="注册" id="id_commit" class="btn btn-primary pull-right">
            </form>
        </div>
    </div>
</div>

<script>
    // 修改头像信息
    $("#myfile").change(function () {
        // 文件阅读器
        // 1.先生成一个文件阅读器对象
        let myFileReaderObj = new FileReader();
        // 2.获取用户上传的头像文件
        let fileObj = $(this)[0].files[0];
        // 3.将文件对象交给阅读器对象读取
        myFileReaderObj.readAsDataURL(fileObj) // 异步提交
        // 4.利用文件阅读器将文件展示到前端页面  修改src属性
        // 等待文件阅读器加载完毕之后再执行
        myFileReaderObj.onload = function () {
            $("#myimg").attr('src', myFileReaderObj.result)
        }
    });

    // 绑定点击事件
    $("#id_commit").click(function () {
        // 发送的数据包中包含着普通的键值也包含着文件
        // 需要用到 ForData对象
        let formData = new FormData();
        // 打印需要获取的信息
        {#console.log($("#myform").serializeArray()) // [{},{},{},{}]#}

        // 1.添加普通的键值对
        $.each($("#myform").serializeArray(), function (index, obj) {
            {#console.log(index, obj)  // obj = {'username': 'jack'}#}
            formData.append(obj.name, obj.value);
        });

        // 2.添加文件数据
        formData.append('avatar', $("#myfile")[0].files[0]);

        // 3.发送Ajax请求
        $.ajax({
            url: '',
            type: 'post',
            data: formData,
            // 需要指定两个关键性的参数
            contentType: false,
            processData: false,

            success: function (res) {
                // alert(res);
                if (res.code === 200) {
                    // 跳转到登录页面
                    window.location.href = res.url;
                } else {
                    // 如何将错误的提示展示到对应的input框下面
                    // forms组件渲染的标签的id值都是 id_字段名
                    $.each(res.msg, function (index, obj) {
                        console.log(index, obj); // username ['用户名不能为空']
                        let targetId = '#id_' + index;
                        $(targetId).next().text(obj[0]).parent().addClass('has-error');
                    });
                }
            }
        });
    });

    // 给所有的input框获取绑定焦点事件
    $("input").focus(function () {
        // 将input下面的span标签和input外面的div标签修改内及属性
        $(this).next().text('').parent().removeClass('has-error');
    });

</script>

</body>
</html>

2.后端

def register(request):
    form_obj = MyRegForm()

    if request.method == 'POST':
        # 校验数据是否合法
        form_obj = MyRegForm(request.POST)

        back_dict = {'code': 200, 'msg': '注册成功,3秒后自动跳转页面'}
        # 判断数据是否合法
        if form_obj.is_valid():

            print(form_obj.cleaned_data)
            # {'username': 'jack', 'password': '123', 'confirm_password': '123', 'email': '[email protected]'}

            # 将校验通过的数据字典赋值给一个变量
            clean_data = form_obj.cleaned_data

            # 将字典里面的confirm_password键值对删除
            clean_data.pop('confirm_password')
            # {'username': 'jack', 'password': '123', 'email': '[email protected]'}

            # 用户头像
            file_obj = request.FILES.get('avatar')
            '''针对用户头像一定要判断是否传值,不能直接添加到字典中去'''
            if file_obj:
                clean_data['avatar'] = file_obj

            # 直接操作数据库保存数据
            models.UserInfo.objects.create_user(**clean_data)
            back_dict['url'] = '/login/'
        else:
            back_dict['code'] = 2000
            back_dict['msg'] = form_obj.errors
        return JsonResponse(back_dict)

    return render(request, 'register.html', locals())

三、登录功能之图片验证码推导操作

1.以登录–前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    {% load static %}
    <script src="{% static 'js/jquery.min.js' %}"></script>
    <link href="{% static 'bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
    <script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
    <script src="{% static 'layer/layer.js' %}"></script>

</head>
<body>

<div class="container-fluid">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h1 class="text-center">登录</h1>
            <div class="form-group">
                <label for="username">用户名</label>
                <input type="text" name="username" id="username" class="form-control">
            </div>
            <div class="form-group">
                <label for="password">密码</label>
                <input type="password" name="password" id="password" class="form-control">
            </div>
            <div class="form-group">
                <label for="username">验证码</label>
                <div class="row">
                    <div class="col-md-6">
                        <input type="text" name="code" id="id_code" class="form-control">
                    </div>
                    <div class="col-md-6">
                        <img src="/get_code/" id="id_img" alt="" width="535" height="35">
                    </div>
                </div>
            </div>
            <input type="button" value="登录" class="btn btn-success btn-block">
        </div>
    </div>
</div>

</body>
</html>

<script>
    // 给图片一个点击事件,以防止二次刷新页面
    $("#id_img").click(function () {
        // 先获取标签之前的srco
        let oldVal = $(this).attr('src');
        $(this).attr('src', oldVal += '?')
    });
</script>


2.python推导图片生成嘛

'''
    图片相关模块
    pip install pillow
    
    Image: 生成图片
    ImageDraw:能够在图片上乱涂乱画
    ImageFont:控制字体样式
    
    BytesIO:临时存储数据,返回的时候是二进制
    StringIO:临时存储数据,返回的时候是字符串
'''
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO, StringIO


# 生成字母数字的随机函数
def get_random():
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)


# 图片验证码相关操作
def get_code(request):
    '''
    图片的src格式:
        图片的路径
        url
        二进制数据
    :param request:
    :return:
    '''
    #推导步骤1:直接获取后端现成的图片二进制数据发送给前端
    with open(r'E:\python26\day66_BBS\avatar\7.jpg', 'rb') as f:
        data = f.read()
    return HttpResponse(data)

    #推导步骤2:利用pillow模块动态的产生图片
    new(mode, size, color=0)
    img_obj = Image.new('RGB', (535, 35), 'green')
    img_obj = Image.new('RGB', (535, 35), get_random())
    # 先将图片对象保存起来
    with open('xxx.png', 'wb') as wf:
        img_obj.save(wf, 'png')

    # 再将图片对象读取出来
    with open('xxx.png', 'rb') as rf:
        data = rf.read()
    return HttpResponse(data)
    
	# 推导步骤3:文件存储繁琐IO操作效率低 借助于内存管理器模块
    img_obj = Image.new('RGB', (535, 35), get_random())
    # 生成一个内存管理器对象,可看做文件句柄
    io_obj = BytesIO()
    img_obj.save(io_obj, 'png')
    # 从内存管理器中读取二进制数据返回给前端
    return HttpResponse(io_obj.getvalue())

	# 最终步骤4:写图片验证码
    img_obj = Image.new('RGB', (535, 35), get_random())
    # 产生一个画笔对象
    img_draw = ImageDraw.Draw(img_obj)
    # 字体样式和大小
    img_font = ImageFont.truetype('static/font/yun.ttf', 30)

    # 随机验证码  五位数的随机验证码 数字 大小写字母
    code = ''
    for i in range(5):
        random_upper = chr(random.randint(65, 90))
        random_lower = chr(random.randint(97, 122))
        random_int = str(random.randint(0, 9))
        # 从上面三个中随机选择一个
        tmp = random.choice([random_upper, random_lower, random_int])
        # 将产生的随机字符串写入图片上
        '''
        为什么一个个写而不是生成好了之后再写?
            因为一个个写能够控制每个字体的间隙 而生成好之后在写的话,没法控制间隙
        '''
        img_draw.text((i * 45 + 60, 2), tmp, get_random(), img_font)
        # 拼接随机字符串
        code += tmp
    print('验证码:', code)
    request.session['code'] = code
    # 随机验证码在登录的视图函数里面要用到 要比对 所以要找到地方存起来并且其他视图函数也能拿到
    io_obj = BytesIO()
    img_obj.save(io_obj, 'png')
    return HttpResponse(io_obj.getvalue())

3.登录功能

def login(request):
    if request.method == 'POST':
        back_dict = {'code': 200, 'msg': '登录成功,3秒后自动跳转页面'}

        username = request.POST.get('username')
        password = request.POST.get('password')
        code = request.POST.get('code')

        # 校验数据
        if request.session.get('code').upper() == code.upper():
            user_obj = auth.authenticate(request, username=username, password=password)
            if user_obj:
                # 保存用户状态
                auth.login(request, user_obj)
                back_dict['url'] = '/home/'
            else:
                back_dict['code'] = 2000
                back_dict['msg'] = '用户名或密码错误'
        else:
            back_dict['code'] = 3000
            back_dict['msg'] = '验证码错误'
        return JsonResponse(back_dict)

    return render(request, 'login.html', locals())

你可能感兴趣的:(django,python01,django,python,windows,pycharm)