BBS仿博客园代码总结1---注册、登录功能详解

项目流程

仿造博客园项目的核心:文章的增删改查
--------------------------------------------

表关系分析:(项目的重中之重环节,表分析好了,后面的业务代码就好写了)
	先确定表的数量 再确定表的基础字段 最后确定表的外键字段
--------------------------------------------

表数量分析
	1.用户表
	2.个人站点表
	3.文章表
	4.文章分类表
	5.文章标签表
	6.点赞点踩表
	7.文章评论表
--------------------------------------------
--------------------------------------------

表字段分析
用户表
		替换auth_user表并扩展额外的字段
			电话号码、头像、注册时间

	个人站点表
		站点名称(jason\lili\kevin)
		站点标题(努力奋斗去他妹的)
		站点样式(css文件)

	文章表
		文章标题
		文章简介
		文章内容
		发布时间

	文章分类表
		分类名称

	文章标签表
		标签名称

	点赞点踩表:记录哪个用户给哪篇文章点了推荐(赞)还是反对(踩)
		用户字段(用户主键)>>>:外键字段
		文章字段(文章主键)>>>:外键字段
		点赞点踩

	文章评论表:记录哪个用户给哪篇文章评论了什么内容
		用户字段(用户主键)>>>:外键字段
		文章字段(文章主键)>>>:外键字段
			评论内容
			评论时间
			外键字段(自关联)

--------------------------------------------

表的外键字段分析:

	用户表
		用户与个人站点是一对一外键关系

	个人站点表

	文章表
		文章表与个人站点表是一对多外键关系
		文章表与文章分类表是一对多外键关系
		文章表与文章标签表是多对多外键关系
		文章评论数
		文章点赞数
		文章点踩数
'''
数据库字段优化设计:我们想统计文章的评论数 点赞数
	通过文章数据跨表查询到文章评论表中对应的数据统计即可
但是文章需要频繁的展示 每次都跨表查询的话效率极低
	我们在文章表中再创建三个普通字段
之后只需要确保每次操作评论表或者点赞点踩表时同步修改上述三	个普通字段即可
'''

	文章分类表
		文章分类与个人站点是一对多外键关系

	文章标签表
		文章标签与个人站点是一对多外键关系

bbs仿博客园项目表关系分析
个人站点和文章分类与文章标签也有一对多的外键关系!!!
image
.
.
.

注册功能

用户注册
	1.渲染前端标签
 	2.校验用户数据
	3.展示错误提示
ps:forms组件、modelform组件

单独开设py文件编写 解耦合!!!

.
.
.

准备工作

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

AUTH_USER_MODEL = 'app01.UserInfo'

--------------------------------------------
# 注册
path('register/', views.register_func),
--------------------------------------------

.

模型层所有的表,该bbs项目的关键中的关键!!!表建不好,什么业务都不好写!!!

from django.db import models

# Create your models here.
from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    """用户表"""
    phone = models.BigIntegerField(verbose_name='手机号', null=True, blank=True)  # blank参数用于控制admin后台管理与数据库无关
    # FileField文件对象字段类型,传文件对象,自动保存到提前配置好的路径下并存储该路径信息!!
    avatar = models.FileField(upload_to='avatar/', default='avatar/1111.jpg', verbose_name='用户头像')
    register_time = models.DateTimeField(verbose_name='注册事件', auto_now_add=True)

    # 外键一对一字段(#千万别建成一对多)
    site = models.OneToOneField(to='Site', on_delete=models.CASCADE, null=True)  # 表名开头别小写了

    # 修改admin后台管理的表名
    class Meta:
        verbose_name_plural = '用户表'

    def __str__(self):
        return f'用户对象<*>{self.username}'


class Site(models.Model):
    """个人站点表"""
    site_name = models.CharField(verbose_name='站点名称', max_length=32)
    site_title = models.CharField(verbose_name='站点标题', max_length=32)
    site_theme_css = models.TextField(verbose_name='站点样式', null=True)
    site_theme_js = models.TextField(verbose_name='站点样式', null=True)

    # 修改admin后台管理的表名
    class Meta:
        verbose_name_plural = '个人站点表'

    def __str__(self):
        return f'个人站点对象<*>{self.site_name}'


class Article(models.Model):
    """文章表"""
    title = models.CharField(verbose_name='文章标题', max_length=32)
    desc = models.CharField(verbose_name='文章简介', max_length=255)
    content = models.TextField(verbose_name='文章内容')
    create_time = models.DateTimeField(verbose_name='创建事件', auto_now_add=True)
    # 三个优化字段
    comment_num = models.IntegerField(verbose_name='评论数', default=0)
    up_num = models.IntegerField(verbose_name='点赞数', default=0)
    down_num = models.IntegerField(verbose_name='点踩数', default=0)

    # 外键一对多字段  一个站点对多个文章,一个分类对多个文章
    site = models.ForeignKey(to='Site', on_delete=models.CASCADE, null=True)
    category = models.ForeignKey(to='Category', on_delete=models.CASCADE, null=True)

    # 外键多对多字段  一个文章可以有多个标签,一个标签可以有多个文章,外键建在查询频率高的表里面
    tags = models.ManyToManyField(to='Tag',
                                  through='Article2Tag',
                                  through_fields=('article', 'tag')
                                  )

    # 修改admin后台管理的表名
    class Meta:
        verbose_name_plural = '文章表'

    def __str__(self):
        return f'文章对象<*>{self.title}'


class Article2Tag(models.Model):
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE, null=True)
    # 外键一对多字段
    tag = models.ForeignKey(to='Tag', on_delete=models.CASCADE, null=True)

    # 修改admin后台管理的表名
    class Meta:
        verbose_name_plural = '文章与标签多对多表'


class Category(models.Model):
    """文章分类表"""
    name = models.CharField(verbose_name='分类名称', max_length=32)
    # 外键一对多字段
    site = models.ForeignKey(to='Site', on_delete=models.CASCADE, null=True)

    # 修改admin后台管理的表名
    class Meta:
        verbose_name_plural = '文章分类表'

    def __str__(self):
        return f'文章分类对象<*>{self.name}'


class Tag(models.Model):
    """文章标签表"""
    name = models.CharField(verbose_name='标签名称', max_length=32)
    # 外键一对多字段
    site = models.ForeignKey(to='Site', on_delete=models.CASCADE, null=True)

    # 修改admin后台管理的表名
    class Meta:
        verbose_name_plural = '文章标签表'

    def __str__(self):
        return f'标签对象<*>{self.name}'


class UpAndDown(models.Model):
    """点赞点踩表"""
    up_or_down = models.BooleanField(verbose_name='点赞点踩')  # 传布尔值  存0或1
    # 外键一对多字段
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE, null=True)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE, null=True)

    # 修改admin后台管理的表名
    class Meta:
        verbose_name_plural = '点赞点踩表'


class Comment(models.Model):
    """文章评论表"""
    content = models.TextField(verbose_name='评论内容')
    comment_time = models.DateTimeField(auto_now_add=True, verbose_name='评论时间')

    # 外键一对多字段
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE, null=True)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE, null=True)

    # 自关联字段
    parent = models.ForeignKey(to='self', on_delete=models.CASCADE, null=True)

    # 修改admin后台管理的表名
    class Meta:
        verbose_name_plural = '文章评论表'

------------------------------------------
表创好了后,mysql创个数据库,settings里面改一下

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '20230113bbs',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': '222',
        'CHARSET': 'utf8'
    }
}

.
.

注册相关代码

注册代码的思路:
首先就是要利用django提供的forms类来进行input标签的渲染与数据的校验
所以要先写好注册的forms类  class RegisterForm(forms.Form):
别忘了局部钩子与全局钩子函数的编写
--------------------------------
当后端将前端发送过来的字典数据通过request.POST拿到后,交给我们自己定义的forms类进行校验,
只要当数据符合字段与正则的要求后,就放到cleaned_data这个字典里面去。

注意钩子函数的触发要满足的前提是,数据符合字段参数的校验与正则的校验后,才能走到触发钩子函数的这一步。

如果字段参数的校验与正则的校验没有问题后,触发钩子函数的运行,如果不符合钩子函数的逻辑校验,通过self.add_error('字段名','错误信息') 给对应字段的数据,添加错误信息
----------------------------------
forms类写好后,要先在后端生成个空对象并传到前端页面,渲染出标签
所以开始写前端页面
首先如果forms类渲染出的input标签很多,那么我们ajax在发送数据的时候要按id一个一个查找input框并获取value值,太繁琐了,所以利用form表单标签的序列化功能,可以一次性拿到所有form标签里面的所有input框里面的数据,但是不要form表单提交请求的功能,所以form标签删掉action="" 加个id属性
------------------------------------
{% for form in form_obj %}
    <div class="form-group">
        <label for="{{ form.auto_id }}">{{ form.label }}</label>
        {{ form }}
        <span style="color: red"></span>
    </div>
{% endfor %}
------------------------------------
用户头像的标签就自己写,不用forms类渲染了
<div class="form-group">
    <label for="myFile">用户头像
        <img src="/static/img/default.jpg/" id="myImg" alt="" style="width: 100px">
    </label>
        <input type="file" id="myFile" style="display: none">
</div>
------------------------------------
最后就是重点的3个事件了
第一个就是文本域变化事件,当头像的input框里面选择过头像图片后,能够实时的展示到页面上的图片标签上
$('#myFile').change(function () {}
先产生一个文件阅读器对象,再将该input框的标签对象转文件对象,拿到用户选择的头像文件
将头像文件交给阅读器对象,等待阅读器对象读完后,再修改img标签的src的属性展示选择的图片
$('#myImg').attr('src', myFileReaderObj.result)

第二个就是点击注册按钮的点击事件,因为要发头像文件的数据给后端,
所以要先  let myFormDataObj = new formdata() 产生一个空对象
然后就是利用form表单serializeArray()一次性获取里面的input标签普通的数据,往空对象里面添加,头像文件也往空对象里面添加,最后发送ajax请求,注意发送文件数据别忘了
contentType : false 和processData :false   不编码也不做任何处理
回调函数success里面的操作下面图片区处有讲解

第三个就是获取焦点事件了,当用户输入的信息有误,并被我们渲染在页面上时,我们需要在用户再次点击输入框的时候,将错误提示信息与输入框的错误样式去掉
    $('input').focus(function () {
        // 事件函数中的this关键字指代的就是当前被操作的标签对象本身
        $(this).next().text('').parent().removeClass('has-error')
    })
------------------------------------
最后就是后端的逻辑判断了
先request.POST获取普通数据,然后交给forms类校验,
然后调用form_obj.is_valid()校验方法完成数据的校验,
用一个变量名接收一下form_obj.cleaned_data里面的符合校验规则的字典数据
然后再去掉字典里面的confirm_password的键对应的值,
再把request.FILES.get('avatar')获取到的头像文件,以键'avatar'添加到字典里面去
最后创建用户数据create_user比create更强,可以给密码加密写到数据库
**将字典打散变成关键字参数的形式写到数据库去
models.UserInfo.objects.create_user(**cleaned_data)
最后把一些小的逻辑判断补一下,注册就结束了

myforms.py 里面代码

from django import forms
from django.core.validators import RegexValidator
from app01 import models


class RegisterForm(forms.Form):
    """注册forms类"""
    username = forms.CharField(max_length=9, min_length=3,
                               label='用户名',
                               error_messages={
                                   'max_length': '最长不能超过9位',
                                   'min_length': '最短不低于3位',
                                   'required': '用户名不能位空'
                               },
                               widget=forms.widgets.TextInput(attrs={'class': 'form-control'})
                               )

    password = forms.CharField(max_length=9, min_length=3,
                               label='密码',
                               error_messages={
                                   'max_length': '最长不能超过9位',
                                   'min_length': '最短不低于3位',
                                   'required': '用户名不能位空'
                               },
                               widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
                               )

    confirm_password = forms.CharField(max_length=9, min_length=3,
                                       label='确认密码',
                                       error_messages={
                                           'max_length': '最长不能超过9位',
                                           'min_length': '最短不低于3位',
                                           'required': '用户名不能位空'
                                       },
                                       widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
                                       )

    phone = forms.CharField(label='手机号码',
                            validators=[
                                RegexValidator(r'^[0-9]+$', '请输入数字'),
                                RegexValidator(r'^1(3[0-9]|4[0-9]|5[0-9])\d{8}$', '号码不符合要求')
                            ],
                            widget=forms.widgets.TextInput(attrs={'class': 'form-control'})
                            )

    email = forms.EmailField(label='邮箱',
                             error_messages={
                                 'required': '邮箱不能为空',
                                 'invalid': '邮箱格式不正确'
                             },
                             widget=forms.widgets.EmailInput(attrs={'class': 'form-control'})
                             )

    # 局部钩子
    def clean_username(self):
        username = self.cleaned_data.get('username')
        res = models.UserInfo.objects.filter(username=username).first()
        if res:
            self.add_error('username', '用户名已存在')
        return username

    # 全局钩子
    def clean(self):
        password = self.cleaned_data.get('password')
        confirm_password = self.cleaned_data.get('confirm_password')
        if not password == confirm_password:
            self.add_error('confirm_password', '两次密码不一致')
        return self.cleaned_data

----------------------------------

.
.

视图层代码


def register_func(request):
    # 前后端ajax交互 通常采用字段作为交互对象
    back_dict = {'code': 10000, 'msg': ''}
    # 1.先产生一个空的form_obj,该空对象是用来被locals()传到前端渲染出标签的
    form_obj = myforms.RegisterForm()

    # 整体是前端传回来的数据,进行逻辑判断与处理,并将字典信息传给ajax的异步回调函数
    if request.method == 'POST':
        form_obj = myforms.RegisterForm(request.POST)  # 正常的5个,还有一个csrfmiddlewaretoken
        if form_obj.is_valid():
            clean_data = form_obj.cleaned_data  # 存储符合校验的数据,只有正常的5个键值对数据了
            # 将confirm_password键值对移除
            clean_data.pop('confirm_password')  # 只有正常的4个键值对数据了
            # 获取用户上传的头像数据
            avatar_obj = request.FILES.get('avatar')  # 用户有可能没有上传
            if avatar_obj:
                clean_data['avatar'] = avatar_obj  # 如果头像存在,将文件对象添加到clean_data里面去
            # 创建用户数据
            models.UserInfo.objects.create_user(**clean_data)  # 上述处理字典的目的就是为了创建数据省事
            back_dict['msg'] = '注册成功'
            back_dict['url'] = '/app01/login/'
        else:
            back_dict['code'] = 10001
            back_dict['msg'] = form_obj.errors
        return JsonResponse(back_dict)


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

.
.

模板层代码

# 注册的html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/jQuery3.6.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.min.css">
    <script src='/static/bootstrap-3.4.1-dist/js/bootstrap.min.js'></script>
</head>

<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col_md-set-2">
            <h2 class="text-center">用户注册</h2>
            <form id="form">
            <!--不使用form表单提交数据,但是用form标签 它有一个序列化的功能-->
                {% csrf_token %}

                {% for form in form_obj %}
                    <div class="form-group">
                   <!--目的就是让多个获取用户的标签上下间距更大一些-->

<!--双括号form.auto_id的意思是:自动获取下面标签的id值,现在label标签和input框绑定了,点击字段名也将会聚焦到框里面去了-->

                    <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">头像
               <img src="/static1/img/default.jpg" alt="" width="120" id="myimg" style="margin-left: 30px">   <!--把图片放到label里面,这样点图片就会触发input框了-->
                </label>

                 <input type="file" id="myfile" style="display: none">

            </div>
                <input type="button" id="subBtn" class="btn btn-primary btn-block" value="注册">
            </form>
        </div>
    </div>
</div>

    <script>
        // 用户头像的实时展示,给标签绑定一个域内容变化
        $('#myfile').change(function () {
            // 1. 产生一个文件阅读器对象
            let myFileReaderObj = new FileReader();
            // 2. 获取用户上传的头像文件
            let fileObj = this.files[0]    // 事件函数中的this关键字指代的就是当前被操作的标签对象本身
            // 3. 将文件对象交给阅读器
            myFileReaderObj.readAsDataURL(fileObj); // 异步操作
            // 5. 需要等待文件阅读器对象加载完毕之后再修改src
            myFileReaderObj.onload = function (){

                // 4. 修改img标签的src属性展示图片
            $('#myimg').attr('src',myFileReaderObj.result)
                // attr() 相当于js代码 setAttribute() 给标签添加默认属性或自定义属性
            }
        })


        // 给注册按钮绑定点击事件 发送ajax请求  携带了文件数据
        $('#subBtn').click(function () {
            // 1. 先用js代码产生一个内置对象,帮我们携带文件数据
            let myFormDataObj = new FormData();
            // 2. myFormDataObj.append('username',$('#id_username')) 单个单个添加普通数据效率太低

            {#console.log($('#form').serializeArray())  // 可以一次性获取form标签内所有普通字段数据[{},{},{}]#}
            {#  对结果for循环,然后交给后面的函数处理,行参index接收的是索引,行参dataObj接收到的是对象       #}
            $.each($('#form').serializeArray(),function (index,dataObj) {
                myFormDataObj.append(dataObj.name,dataObj.value)
            })

            // 3. 添加头像文件数据
            myFormDataObj.append('avatar',$('#myfile')[0].files[0])

            // 4. 发送ajax请求
            $.ajax({
                url:'',
                type:'post',
                data:myFormDataObj,

                contentType:false,
                processData:false,

                success:function (args) {
                    if (args.code===10000){
                        window.location.href = args.url   // 注册成功跳转登录路由
                    }else{
          // args.msg是一个自定义对象,就是个大字典,里面字段名是键,错误提示是值
         // 如何针对性的渲染错误提示 {'username':[错误提示]}
        // 我们发现forms类渲染出来的input框标签的id为   id_对应的字段名

                        $.each(args.msg,function (k,msgArray) {
                            // 拼接标签的id值

                            let eleId = '#id_' + k
           // 根据id查找标签 修改下面span标签的内容 并给父标签添加错误样式
                $(eleId).next().text(msgArray[0]).parent().addClass('has-error')
                        })
                    }
                }
            })
        })


        // 给所有的input标签绑定获取焦点事件 移除错误样式
        $('input').focus(function () {
            $(this).next().text('').parent().removeClass('has-error')
        })

    </script>
</body>
</html>
---------------------------------------------

$('#myForm').serializeArray() 一次性获取form标签内的所有普通字段数据,拿到一个列表套字典的数据,每一个字典里面是一个索引为键,值又是一个一个字典的结构。最里面的字典里面才是name对应的是字段名,value对应的是用户写在对应的input框里面的数据
image
.
image
.
.
.
登录页面啥都不写,前端console.log(args.msg)的结果
每个字典里面键是字段名,值是被列表套着的错误的信息
那么我们如何将这些错误的信息渲染到对应的input标签下面的span标签里面去了?
我们发现forms组件渲染出来的input标签的id就是对的id_字段名
这样我们只需要将字段名前面拼接出#id_ 那么我们就可以利用拼接后的值来查找到对应的input标签,再.next()就找到对应的下面的span标签了
再点parent()就能找到input标签的父标签了
这个地方因为直接给input标签添加样式类没效果,所以给包裹它的父标签加了
image
.
image
.
image
.
.
.
.
.
.
.
.
.
.

登录功能

img标签的src属性
	1.可以直接填写图片地址
	2.还可以填写一个路由 会自动朝该路由发送get请求
    如果结果是图片的二进制数据 那么自动渲染图片

pip install pillow -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
下载pillow模块时,如果提示不信任源复制最后代码 --trusted-host mirrors.aliyun.com

空一格,粘贴到原来的代码后面,把整行代码到Terminal里面粘贴一下,pip后面加个3.8 区分不同的解释器的pip文件
pip3.8 install --user --index-url http://mirrors.aliyun.com/pypi/simple/ pillow --trusted-host mirrors.aliyun.com

.
.
.

登录相关代码

登录的难点就在获取验证码那边,先用get_random()函数获取一个元组包含30-255的随机数用来控制生成的图片对象的背景颜色
将图片对象交给画笔对象。
再生成一个字体样式对象。
利用random模块与range以及chr()与str()方法产生随机大小写字母与数字的字符,并每次随机取一个
最后利用画笔对象将字符画到图片上面去
draw_obj.text(位置、字符、字符大小)
最后for循环完,画笔以及在图片上画完所有的字符后,利用内存管理器对象将图片对象保存到内存中,最后再从内存中取出该图片通过HttpResponse返回给前端页面

io_obj = BytesIO()
img_obj.save(io_obj, 'png')
return HttpResponse(io_obj.getvalue())
-------------------------------------
登录视图函数的逻辑判断
获取从前端ajax发送过来的post请求数据,拿到用户输入的用户名、密码、验证码。
先判断验证码是否正确,由于用户输入的验证码可能不一定区分大小写字母,所以统一将用户的验证码统一转成大写或小写字母,然后将存在session表里面的验证码也统一转换成大写或小写,进行比对。

再利用auth.authenticate()判断用户名与密码是否正确,如果正确一定不要忘了auth.login(request, user_obj)  确认登录成功,保存用户的登录状态,将用户对象记到session表里!!

最后将不符合的情况逻辑补全,并补全不同情况下的字典收据,通过JsonResponse传给前端ajax的异步回调函数处理!!!
-------------------------------------
前端页面代码的编写要点:
首先把用户登录要输入的input框标签都写好
再弄一个button的登录按钮
最后来两个点击事件,
一个是点击a标签的文字,更换随机验证码,注意要用return false取消掉a标签的跳转功能。
第二个就是点击提交按钮,用ajax将前端标签框里面的数据,发送给后端,并将后端传过来的数据用异步回调函数处理,因为登录要输入的数据不多,所以可以直接利用ifelif判断code码来给对应的input框下面的空span标签渲染内容,或者简单点直接利用alert(args.msg)将错误信息以弹出框的形式弹出来
    path('login/', views.login_func, name='login_view'),
    # 图片验证码相关功能
    path('get_code/', views.get_code_func),
---------------------------------------------
# 视图层代码

def login_func(request):
    back_dict = {'code': 10000, 'msg': ''}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        print(username, password)
        code = request.POST.get('code')
        # 先判断验证码忽略大小写,再判断用户名与密码
        if code.upper() == request.session.get('code').upper():
            user_obj = auth.authenticate(request, username=username, password=password)
            if user_obj:
                # 登录成功后,保存用户的登录状态,将用户对象记到session表里,这句话执行成功,就可以使用request.user获取登录用户对象
                auth.login(request, user_obj)
                back_dict['msg'] = '登录成功'
                back_dict['url'] = '/app01/home/'
            else:
                back_dict['msg'] = '用户名或密码不正确'
                back_dict['code'] = 10001
        else:
            back_dict['code'] = 10002
            back_dict['msg'] = '验证码不正确'
        return JsonResponse(back_dict)

    return render(request, 'loginPage.html')
---------------------------------------------

from PIL import Image, ImageFont, ImageDraw
from io import BytesIO, StringIO

"""
Image      产生图片
ImageFont    字体样式
ImageDraw      画笔对象

BytesIO      在内存中临时存储 读取的时侯以bytes格式为准
StringIO     在内存中临时存储 读取的时侯以字符串格式为准
"""
import random

def get_random():
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)


def get_code_func(request):
    # 1.推导步骤1: 直接读取图片文件返回
    # with open(r'D:\pythonProject\djangoBBS\app01\avatar\3333.jpg','rb') as f:
    #     data = f.read()
    # return HttpResponse(data)
    ----------------------------------
    # 2.推导步骤2:随机产生图片动态返回 pillow模块
    # img_obj = Image.new('RGB', (350, 35), 'blue')  # 产生一个宽350,高35,红色的图片
    # with open(r'XXX.png', 'wb') as f:
    #     img_obj.save(f, 'png')
    # with open(r'xxx.png', 'rb') as f:
    #     data = f.read()
    # return HttpResponse(data)
    ----------------------------------
    # 3. 推导步骤3:针对图片的保存与读取做优化,内存管理
    # img_obj = Image.new('RGB', (350, 35), 'red')
    # io_obj = BytesIO()   产生一个内存管理器对象
    # img_obj.save(io_obj,'png')  把图片对象通过内存管理器对象以png格式存到内存里面
    # return HttpResponse(io_obj.getvalue())
    # 通过内存管理器对象从内存里面拿出刚刚存进去的对象
    ----------------------------------
    # 4. 推导步骤4:图片颜色可以随机变换
    # img_obj = Image.new('RGB', (350, 35), get_random())
    # io_obj = BytesIO()
    # img_obj.save(io_obj, 'png')
    # return HttpResponse(io_obj.getvalue())
    ----------------------------------

    # 5. 推导步骤5:编写验证码
    img_obj = Image.new('RGB', (350, 35), get_random())
    draw_obj = ImageDraw.Draw(img_obj)  # 将图片对象交给画笔对象
    font_obj = ImageFont.truetype('static/font/111.ttf', 35)  # 确定字体样式(ttf文件与字体尺寸)

    # 产生随机验证码
    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(1, 9))
        # 三选一
        temp_choice = random.choice([random_upper, random_lower, random_int])
        # 元组(i * 45 + 45, 0) 第一个参数代表在X轴的位置  第二个产生代表在y轴的位置  所以每次循环写在x轴的位置都不一样
        draw_obj.text((i * 45 + 45, 0), temp_choice, font=font_obj)  # text(水平与竖直位置、字符、字符大小)
        code += temp_choice

    # 后端保存验证码 便于后续的比对
    request.session['code'] = code  # 也可以在全局定义一个code变量,然后在函数里面先申明为global变量,这样在函数里就可改全局变量了
    io_obj = BytesIO()
    img_obj.save(io_obj, 'png')
    return HttpResponse(io_obj.getvalue())

---------------------------------------------

.
.
.

登录的模板层代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/jQuery3.6.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.min.css">
    <script src='/static/bootstrap-3.4.1-dist/js/bootstrap.min.js'></script>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h2 class="text-center">用户登录</h2>

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

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

            <!--验证码-->

            <div class="form-group">
                <label for="code">验证码</label>
                <div class="row">
                    <div class="col-md-6">
                        <input type="text" class="form-control" id="code" style="height: 55px">
                    </div>
                    <div class="col-md-6">
                        <img src="/get_code/" alt="" width="350" height="35" id="d111">
                        <h5 class="text-center" id="v111"><a href="">看不清换一张</a></h5>
                    </div>
                </div>
            </div>
            <input type="button" id="subBtn" class="btn btn-block btn-primary" value="用户登录">
            </form>
        </div>
    </div>
</div>


<script>
    // 换验证码 点击事件
    $('#v111').click(function () {
        let oldSrc = $('#d111').attr('src')
        $('#d111').attr('src', oldSrc + '?')
        return false  // 取消a标签的跳转功能
    })

    // 登录按钮点击事件
    $('#subBtn').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            data: {
                'username': $('#username').val(),
                'password': $('#password').val(),
                'code': $('#code').val(),
                'csrfmiddlewaretoken': '{{ csrf_token }}'
            },
            success: function (args) {
                if (args.code === 10000) {
                    window.location.href = args.url
                } else {
                    alert(args.msg)
                }
            }
        })
    })

</script>

</body>
</html>
posted @   tengyifan  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示