第一篇:BBS表设计及注册登录功能实现

第一篇:BBS表设计及注册登录功能实现

bbs是一个前后端不分离的全栈项目,前端和后端都需要我们自己一步一步完成。

一、表创建及同步

1、表设计

一个项目中最重要的不是业务逻辑的书写,而是前期的表设计,只要将表设计好了,后续的功能书写才会一帆风顺。

我们分析博客园,了解到我们需要建立7张表。

"""1.用户表"""
使用userInfo替代原有的auth_user表,继承AbstractUser
    # 拓展:
	phone 电话号码
    avatar  用户头像
    create_time  创建时间
    # 外键字段
    一对一个人站点表
    
"""2.个人站点表"""
    # 普通字段
    site_name 站点名称
    site_title 	 站点标题
    site_theme	站点样式
     
"""3.文章分类表"""
    # 普通字段 
    name	标签名
    # 外键字段
    一对多个人站点
    
"""4.文章标签表"""
    # 普通字段 
    name	标签名
    # 外键字段
    一对多个人站点
    
"""5.文章表"""
    # 普通字段 
    title	文章标题
    desc	文章简介
    content	文章内容
    create_time 发布时间
    # 数据库字段设计优化(******)(虽然下述的三个字段可以从其他表里面跨表查询计算得出,但是频繁跨表效低)
    up_num	 点赞数
    down_num  点踩数
    comment_num 评论数
    # 外键字段
    一对多个人站点
    多对多文章标签
    一对多文章分类
    
"""6.点赞点踩表"""
记录哪个用户给哪篇文章点了赞还是点了踩
     # 外键字段
    user	ForeignKey(to="User")
    article	ForeignKey(to="Article")
    is_up	BooleanField()
    """
    eg:  1		 1		 1
	 1		 2		 1
	 1		 3		 0
	 2		 1		 1
    """
    
"""7.文章评论表"""
记录哪个用户给哪篇文章写了哪些评论内容
	user	ForeignKey(to="User")	
    article	 ForeignKey(to="Article")
    content	 CharField()
    comment_time  DateField()
    # 自关联
    # parent	ForeignKey(to="Comment",null=True)
    # ORM专门提供的自关联写法	
    parent		ForeignKey(to="self",null=True)
    """
    eg: id   use_id   article_id  parent
    	1      1          1         
    	2      2          1         1
    
    """

以图片的形式进行表示。

2、表创建

由于django自带的sqlite数据库对日期不敏感,所以我们使用mysql数据库。

"""models.py"""
from django.db import models

# Create your models here.


from django.contrib.auth.models import AbstractUser


# 1.用户表
class UserInfo(AbstractUser):
    # 手机号
    phone = models.BigIntegerField(verbose_name='手机号', null=True)
    # 头像
    """给avatar字段传文件对象 该文件会自动存储到avatar文件下 然后avatar字段只保存文件路径avatar/default.png"""
    avatar = models.FileField(verbose_name='头像', upload_to='avatar/', default='avatar/default.png')
    # 创建时间
    create_time = models.DateField(verbose_name='创建时间', auto_now_add=True)
    # 一对一个人站点表
    blog = models.OneToOneField(to='Blog', null=True)


# 2.个人站点表
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)


# 3.文章分类表
class Category(models.Model):
    # 分类名
    name = models.CharField(verbose_name='分类名', max_length=32)
    """一对多个人站点"""
    blog = models.ForeignKey(to='Blog', null=True)


# 4.文章分类表
class Tag(models.Model):
    # 标签名
    tag = models.CharField(verbose_name='标签名', max_length=32)
    """一对多个人站点"""
    blog = models.ForeignKey(to='Blog', null=True)


# 5.文章表
class Article(models.Model):
    # 文章标题
    title = models.CharField(verbose_name='文章标题',max_length=64)
    # 文章简介
    desc = models.CharField(verbose_name='文章简介',max_length=255)
    # 文章内容  有很多文章内容,一般情况下都是使用TextField
    content = models.TextField(verbose_name='文章内容')
    # 创建时间
    create_time = models.DateField(verbose_name='创建时间', 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')


# 6.点赞点踩表
class UpAndDown(models.Model):
    # 哪个用户
    user = models.ForeignKey(verbose_name='哪个用户', to='UserInfo')
    # 那篇文章
    article = models.ForeignKey(verbose_name='那篇文章', to='Article')
    # 是否点赞
    is_up = models.BooleanField(verbose_name='是否点赞')  # 传布尔值 存0/1


# 7.文章评论表
class Comment(models.Model):
    # 那个用户
    user = models.ForeignKey(verbose_name='哪个用户', to='UserInfo')
    # 那篇文章
    article = models.ForeignKey(verbose_name='哪篇文章', to='Article')
    # 评论内容
    content = models.CharField(verbose_name='评论内容', max_length=255)
    # 评论时间
    comment_time = models.DateTimeField(verbose_name='评论时间', auto_now_add=True)
    # 自关联
    parent = models.ForeignKey(to='self', null=True)  # 有些评论本身就是根评论

创建结果如下所示。

二、注册功能

注册用户需要用到forms组件,我们之前是直接在views.py中书写的forms组件代码,但是为了接耦合,应该将所有的forms组件代码单独写到一个地方。

如果项目至始至终只用到一个forms组件,那么你可以直接建一个py文件书写即可,即myform.py, 但是如果项目需要使用多个forms组件,那么你可以创建一个文件夹,在文件夹内根据forms组件功能的不同,来创建不同的py文件。

myforms文件夹
  -- regform.py
  -- loginform.py
  -- userform.py
  -- orderform.py
  ...

在这里我们只在应用下创建一个myforms.py文件,方便书写。

注册功能代码如下。

  • myforms.py
# 书写针对用户表的forms组件代码
from django import forms
from app01 import models


class MyRegForm(forms.Form):
    # 用户名
    username = forms.CharField(
        label='用户名',
        min_length=3,
        max_length=8,
        error_messages={
            'required': '用户名不能为空',
            'min_length': "用户名最少3位",
            'max_length': "用户名最大8位"
        },
        widget=forms.widgets.TextInput(
            # 需要让标签有bootstrap样式
            attrs={'class': 'form-control'}
        ),
    )
    # 用户密码
    password = forms.CharField(
        label='密码',
        min_length=3,
        max_length=8,
        error_messages={
            'required': '密码不能为空',
            'min_length': "密码最少3位",
            'max_length': "密码最大8位"
        },
        widget=forms.widgets.PasswordInput(
            # 需要让标签有bootstrap样式
            attrs={'class': 'form-control'}
        )
    )
    # 确认密码
    confirm_password = forms.CharField(
        label='确认密码',
        min_length=3,
        max_length=8,
        error_messages={
            'required': '确认密码不能为空',
            'min_length': "确认密码最少3位",
            'max_length': "确认密码最大8位"
        },
        widget=forms.widgets.PasswordInput(
            # 需要让标签有bootstrap样式
            attrs={'class': 'form-control'}
        )
    )
    # 邮箱
    email = forms.CharField(
        label='邮箱',
        error_messages={
            'required': '邮箱不能为空',
            'invalid': '邮箱格式不正确'
        },
        widget=forms.widgets.EmailInput(
            # 需要让标签有bootstrap样式
            attrs={'class': 'form-control'}
        )
    )

    # 局部钩子:校验用户名是否已存在
    def clean_username(self):
        username = self.cleaned_data.get('username')
        # 去数据库中校验
        is_exist = models.UserInfo.objects.filter(username=username).first()
        if is_exist:
            # 提示信息
            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
  • register.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <!--这里我们不用form表单提交数据,使用ajax,只是单纯的用一下form标签而已-->
            <h1 class="text-center">注册功能</h1>
            <form id="myform">
                <!--csrf校验[即便下面ajax也会传递随机字符串,这里也不能取消]-->
                {% csrf_token %}
                <!--form组件input框-->
                {% for form in form_obj %}
                    <div class="form-group">
                        <!--form.auto_id能够直接拿到的form里面input框的id值-->
                        <label for="{{ form.auto_id }}">{{ form.label }}</label>
                        {{ form }}
                        <!-- ajax提交form表单,span标签里面书写没用-->
                        <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" alt="" width="100px" style="margin-left: 10px">
                    </label>
                    <!--隐藏输入文件框-->
                    <input type="file" name="avatar" style="display: none" id="myfile">
                </div>
                <!--input提交按钮,不适用submit,使用ajax-->
                <input type="button" class="btn btn-primary pull-right" value="注册" id="id_commit">
            </form>
        </div>
    </div>
</div>

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

    // ajax提交form表单,并返回相关信息
    $('#id_commit').click(function () {
        // 发送ajax请求     我们发送的数据中即包含普通的键值也包含文件
        let formDataObj = new FormData();
        // 1.添加普通的键值对
        {#console.log($('#myform').serializeArray())  // [{name: "username", value: "yangyi"},{{name: "password", value: "123"}}, ...]  只包含普通键值对#}
        /*
        * [{name: "csrfmiddlewaretoken", value: "4ISNTAFRHyjx3KUPVZmeLzc4BqKq5hFqdcTNLRhrsGnfmX9NsaMTIAh1pvrfSVU8"},
        *  {name: "username", value: "yangyi"},
        *  {name: "password", value: "123"},
        *  ...
        * ]
        * */
        $.each($('#myform').serializeArray(),function (index,obj) {
            {#console.log(index,obj)#}  // obj = {}
            formDataObj.append(obj.name,obj.value)
        });
        // 2.添加文件数据
        formDataObj.append('avatar',$('#myfile')[0].files[0]);

        // 3.发送ajax请求
        $.ajax({
            url:"",
            type:'post',
            data:formDataObj,

            // 需要指定两个关键性的参数
            contentType:false,
            processData:false,

            success:function (args) {
                // args为ajax返回的对象,已经反序列化,为object
                if (args.code==1000){
                    // 跳转到登陆页面
                    window.location.href = args.url
                }else{
                    // 如何将对应的错误提示展示到对应的input框下面
                    // forms组件渲染的标签的id值都是 id_字段名
                    $.each(args.msg,function (index,obj) {
                        {#console.log(index,obj)  //  username   ["用户名不能为空"]#}
                        let targetId = '#id_' + index;
                        // jQuery的链式操作【$(targetId).next()拿到span标签的jQuery格式】【.parent() 拿到input】
                        $(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>
  • views.py
from django.shortcuts import render, HttpResponse
from app01.myforms import MyRegForm  # 模块必须全名
from app01 import models
from django.http import JsonResponse

# Create your views here.


# 注册功能
def register(request):
    form_obj = MyRegForm()
    if request.method == 'POST':
        # print(request.POST)  # <QueryDict: {'csrfmiddlewaretoken': ['QAjwUsyzwKZzcT61ECviILzDQQXX3PnWZ4kwMJa9hS3hv6lZbNVXFMEAEVEMQtCE'], 'username': ['yangyi'], 'password': ['123'], 'confirm_password': ['123'], 'email': ['123@qq.com']}>
        # ajax返回的字典
        back_dic = {"code": 1000, 'msg': ''}
        # 校验数据是否合法
        form_obj = MyRegForm(request.POST)
        # 判断数据是否合法
        if form_obj.is_valid():
            print(form_obj.cleaned_data)  # {'username': 'yangyi', 'password': '123', 'confirm_password': '123', 'email': '123@qq.com'}
            clean_data = form_obj.cleaned_data  # 将校验通过的数据字典赋值给一个变量
            # 将字典里面的confirm_password键值对删除
            clean_data.pop('confirm_password')  # {'username': 'yangyi', 'password': '123', 'email': '123@qq.com'}
            # 用户头像
            file_obj = request.FILES.get('avatar')
            """
            针对用户头像一定要判断是否传值,不能直接添加到字典里面去
            因为如果不传值,会将本来的default的图像变为none
            """
            if file_obj:
                clean_data['avatar'] = file_obj
            # 直接操作数据库保存数据
            models.UserInfo.objects.create_user(**clean_data)
            back_dic['url'] = '/login/'
        # 如果数据不合法
        else:
            back_dic['code'] = 2000
            back_dic['msg'] = form_obj.errors   # form_obj.errors 字典套列表形式,类似{'username': ['用户名不能为空']}
        # ajax返回字典
        return JsonResponse(back_dic)
    return render(request,'register.html',locals())


# 登录功能
def login(request):
    return HttpResponse('登陆界面')

最终效果如下所示。

数据库中数据为

文件目录结构为

三、登录功能

1、验证码测试

测试验证码的前端代码【关于验证码字体,我们可以访问字体网站

"""login.html"""

<div class="col-md-6">
    <!--每次刷新页面的时候,向/get_code/路径发送get请求-->
    <!--img标签的src属性:1.图片路径 2.url 3.图片的二进制数据-->
    <img src="/get_code/" alt="" width="430" height="35" id="id_img">
</div>

<script>
    // 随机生成验证码
    $('#id_img').click(function () {
        // 生成一个路径,加?即可
        let new_url = $(this).attr('src') + '?'
        $(this).attr('src', new_url)
    })
</script>

测试验证码的后端代码

"""第一种方式 直接获取后端现成的图片二进制数据发送给前端"""
def get_code(request):
    with open('static/img/美女.jpg', mode='rb') as f:
        data = f.read()
    return HttpResponse(data)


"""第二种方式 利用pillow模块动态产生图片"""
"""
使用pillow模块:图片相关模块
   -- Image:生成图片
   -- ImageDraw:能够在图片上乱涂乱画
   -- ImageFont:控制字体样式
"""

# 随机生成三基色
def get_random():
    import random
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)

from PIL import Image, ImageDraw, ImageFont  # pillow模块导入方式比较特殊
def get_code(request):
    # 第二种方式 利用pillow模块动态产生图片
    # img_obj = Image.new('RGB', (430, 35), 'green')
    img_obj = Image.new('RGB', (430, 35), get_random())
    # 将图片对象保存
    with open('static/img/xxx.png', 'wb') as f:
        img_obj.save(f, 'png')
    with open('static/img/xxx.png', 'rb') as f:
        data = f.read()
    return HttpResponse(data)


"""第三种方式 文件存储繁琐IO操作效率低,借助于内存管理器模块"""
"""
内存管理器模块
BytesIO:临时帮你存储数据,返回的时候数据是二进制
StringIO:临时帮你存储数据,返回的时候数据是字符串
"""
def get_code(request):
    # 生成一个图片文件
    img_obj = Image.new('RGB', (430, 35), get_random())
    # 生成一个内存管理器对象,你可以看成是文件句柄
    io_obj = BytesIO()
    img_obj.save(io_obj, 'png')
    return HttpResponse(io_obj.getvalue())


"""第四种方式:写图片验证码"""
def get_code(request):
    # 生成图片对象
    img_obj = Image.new('RGB', (430, 35), get_random())
    # 在图片上生成画笔对象
    img_draw = ImageDraw.Draw(img_obj)
    # 图片字体设置 【我们的计算机上面致所有能够输出各式各样的字体样式,内部其实对应的是.ttf或.otf结尾的文件】
    img_font = ImageFont.truetype('static/font/222.ttf', 30)

    # 生成随机验证码【5位】
    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*60+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())

好了,到了现在,我们可以实现图片验证码功能,效果如下。

2、完整登录代码

  • login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.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组件,使用ajax提交数据-->
            <!--用户名-->
            <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">
                <div class="row">
                    <!--输入框-->
                    <div class="col-md-6">
                         <label for="id_code">验证码</label>
                        <input type="text" name="code" id="id_code" class="form-control">
                    </div>
                    <!--图片验证码-->
                    <div class="col-md-6" style="padding-top: 25px;">
                        <!--每次刷新页面的时候,向/get_code/路径发送get请求-->
                        <!--img标签的src属性:1.图片路径 2.url 3.图片的二进制数据-->
                        <img src="/get_code/" alt="" width="430" height="35" id="id_img">
                    </div>
                </div>
            </div>
            <!--登录按钮-->
            <input type="button" class="btn btn-success" value="登陆" id="id_commit">
            <span style="color:red;" id="id_span"></span>
        </div>
    </div>
</div>

<script>
    // 随机生成验证码
    $('#id_img').click(function () {
        // 生成一个路径,加?即可
        let new_url = $(this).attr('src') + '?'
        $(this).attr('src', new_url)
    })

    // button按钮提交数据请求
    $('#id_commit').click(function () {
        // ajax请求
        $.ajax({
            url: '/login/',  // 提交给login函数
            type: 'post',
            data:{
                'username': $('#username').val(),
                'password': $('#password').val(),
                'code': $('#id_code').val(),
                'csrfmiddlewaretoken': '{{ csrf_token }}'  // csrf校验,注意加 ''
            },
            success:function (args) {
                if(args.code === 1000){
                    // 跳转到如果登陆成功,跳转到home页面
                    window.location.href = args.url
                }else{
                    $('#id_span').text(args.msg)
                }
            }
        })
    })
</script>
</body>
</html>
  • views.py
from django.shortcuts import render, HttpResponse, redirect
from app01 import models
from django.contrib import auth
from django.http import JsonResponse
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO, StringIO

# 登录功能
def login(request):
    # 如果是ajax请求
    if request.is_ajax():
        if request.method == 'POST':
            # 返回给ajax的字典
            back_dic = {'code': 1000, 'msg': ''}
            # print(request.POST)  # <QueryDict: {'username': ['leichao'], 'password': ['123'], 'code': ['mN4ms'], 'csrfmiddlewaretoken': ['wD2DsF3rmZKgTTPfJWdC9tay3pT9qUUSF73DkWF177OYc64dg7Dh6ufvRuAYdy9A']}>
            username = request.POST.get('username')
            password = request.POST.get('password')
            code = request.POST.get('code')
            # 比对验证码是否正确
            if code == request.session.get('code'):
                # 比对用户名密码是否正确【auth模块】【有则返回用户对象,没有返回None】
                user_obj = auth.authenticate(username=username, password=password)
                # 判断用户是否存在
                if user_obj:
                    # 登录用户【即用户信息保存到session中】
                    auth.login(request, user_obj)
                    # 补充字典信息【返回一个url】
                    back_dic['url'] = '/home/'
                else:
                    back_dic['code'] = 2000
                    back_dic['msg'] = '用户名或密码不正确'
            else:
                back_dic['code'] = 3000
                back_dic['msg'] = '验证码不正确'
            return JsonResponse(back_dic)
    return render(request, 'login.html')


# 随机生成三基色
def get_random():
    import random
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)


# 生成验证码
def get_code(request):
    # 生成图片对象
    img_obj = Image.new('RGB', (430, 35), get_random())
    # 在图片上生成画笔对象
    img_draw = ImageDraw.Draw(img_obj)
    # 图片字体设置
    img_font = ImageFont.truetype('static/font/111.otf', 30)

    # 生成随机验证码【5位】
    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 * 60 + 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())
posted @   YangYi215  阅读(630)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示