Django实践-05Cookie和Session

news2024/11/24 19:35:06

文章目录

  • Django实践-05Cookie和Session
    • Django实践-05Cookie和Session
      • 用户登录的准备工作
        • 1. 创建用户模型。
        • 2. 正向工程生成数据库表
        • 3.写utils.py文件,密码转md5
        • 4.给数据表tb_user中插入测试数据
        • 5.编写用户登录的视图函数和模板页。
        • 6.编写urls.py。
        • 6.增加login.html模板页:
      • 实现用户跟踪
      • Django框架对session的支持
      • 实现用户登录验证
        • 生成验证码随机数
        • 添加字体与验证码图片类Captcha
        • 修改polls/views.py文件处理验证码请求与修改登录请求
        • 修改polls/views.py文件,logout函数
        • 修改polls/views.py文件praise_or_criticize函数,限制只有登录的用户才能投票
        • 修改teachers.html,如果没有登录,跳转到登录页
      • 实现用户注册
        • 修改polls/views.py添加register函数
        • 修改urls.py文件,添加注册路由
        • 创建templates/register.html
    • 总结

在这里插入图片描述

Django实践-05Cookie和Session

官网:https://www.djangoproject.com/
博客:https://www.liujiangblog.com/
本博客内容参考git:https://gitcode.net/mirrors/jackfrued/Python-100-Days 一些细节问题,大家可以查看git连接。本文主要的改变为把代码升级为django4.1版本。

Django静态文件问题备注:
参考:
Django测试开发-20-settings.py中templates配置,使得APP下的模板以及根目录下的模板均可生效
解决django 多个APP时 static文件的问题

django配置app中的静态文件步骤
Django多APP加载静态文件

django.short包参考:https://docs.djangoproject.com/en/4.1/topics/http/shortcuts/

Django实践-05Cookie和Session

我们继续来完成上一章节中的项目,实现“用户登录”的功能,并限制只有登录的用户才能投票。

用户登录的准备工作

1. 创建用户模型。

之前我们讲解过如果通过Django的ORM实现从二维表到模型的转换(反向工程),这次我们尝试把模型变成二维表(正向工程)。

class User(models.Model):
    """用户"""
    no = models.AutoField(primary_key=True, verbose_name='编号')
    username = models.CharField(max_length=20, unique=True, verbose_name='用户名')
    password = models.CharField(max_length=32, verbose_name='密码')
    tel = models.CharField(max_length=20, verbose_name='手机号')
    reg_date = models.DateTimeField(auto_now_add=True, verbose_name='注册时间')
    last_visit = models.DateTimeField(null=True, verbose_name='最后登录时间')

    class Meta:
        db_table = 'tb_user'
        verbose_name = '用户'
        verbose_name_plural = '用户'

2. 正向工程生成数据库表

使用下面的命令生成迁移文件并执行迁移,将User模型直接变成关系型数据库中的二维表tb_user。

python manage.py makemigrations polls

输出为:

E:\vscode\vip3-django\djangoproject>python manage.py makemigrations polls
Migrations for ‘polls’:
polls\migrations\0002_user.py
- Create model User

python manage.py migrate polls

输出为:

E:\vscode\vip3-django\djangoproject>python manage.py migrate polls
Operations to perform:
Apply all migrations: polls
Running migrations:
Applying polls.0002_user… OK

3.写utils.py文件,密码转md5

我们在应用下增加一个名为utils.py的模块用来保存需要使用的工具函数。Python标准库中的hashlib模块封装了常用的哈希算法,包括:MD5、SHA1、SHA256等。下面是使用hashlib中的md5类将字符串处理成MD5摘要的函数如下所示。

import hashlib

def gen_md5_digest(content):
    return hashlib.md5(content.encode()).hexdigest()

if __name__=="__main__":
    print("admin123456-->{}".format(gen_md5_digest("admin123456")))
    # admin123456-->a66abb5684c45962d887564f08346e8d

4.给数据表tb_user中插入测试数据

MD5消息摘要算法是一种被广泛使用的密码哈希函数(散列函数),可以产生出一个128位(比特)的哈希值(散列值),用于确保信息传输完整一致。在使用哈希值时,通常会将哈希值表示为16进制字符串,因此128位的MD5摘要通常表示为32个十六进制符号。

insert into `tb_user`
    (`username`, `password`, `tel`, `reg_date`)
values
    ('user1', 'a66abb5684c45962d887564f08346e8d', '13122334455', now()),
    ('user2', 'a66abb5684c45962d887564f08346e8d', '13890006789', now());

说明:上面创建的两个用户user1和user2密码是admin123456。

5.编写用户登录的视图函数和模板页。

在polls/views.py添加渲染登录页面的视图函数:

from django.http import HttpRequest, HttpResponse


def login(request: HttpRequest) -> HttpResponse:
    hint = ''
    return render(request, '/login.html', {'hint': hint})

6.编写urls.py。

在urls.py文件中添加路由

    path('login/', polls_views.login),# 登录
    path('logout/', polls_views.logout),# 注销
    path('captcha/', polls_views.get_captcha),# 验证码
    path('register/', polls_views.logout),# 注册

6.增加login.html模板页:

CSRF的作用,参考
本博客内容参考git:https://gitcode.net/mirrors/jackfrued/Python-100-Days
在templates目录下创建login.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
    <style>
        #container {
            width: 520px;
            margin: 10px auto;
        }
        .input {
            margin: 20px 0;
            width: 460px;
            height: 40px;
        }
        .input>label {
            display: inline-block;
            width: 140px;
            text-align: right;
        }
        .input>img {
            width: 150px;
            vertical-align: middle;
        }
        input[name=captcha] {
            vertical-align: middle;
        }
        form+div {
            margin-top: 20px;
        }
        form+div>a {
            text-decoration: none;
            color: darkcyan;
            font-size: 1.2em;
        }
        .button {
            width: 500px;
            text-align: center;
            margin-top: 20px;
        }
        .hint {
            color: red;
            font-size: 12px;
        }
    </style>
</head>
<body>
    <div id="container">
        <h1>用户登录</h1>
        <hr>
        <p class="hint">{{ hint }}</p>
        <form action="/login/" method="post">
            {% csrf_token %}
            <fieldset>
                <legend>用户信息</legend>
                <div class="input">
                    <label>用户名:</label>
                    <input type="text" name="username">
                </div>
                <div class="input">
                    <label>密码:</label>
                    <input type="password" name="password">
                </div>
                <div class="input">
                    <label>验证码:</label>
                    <input type="text" name="captcha">
                    <img id="code" src="/captcha/" alt="" width="150" height="40">
                </div>
            </fieldset>
            <div class="button">
                <input type="submit" value="登录">
                <input type="reset" value="重置">
            </div>
        </form>
        <div>
            <a href="/">返回首页</a>
            <a href="/register/">注册新用户</a>
        </div>
    </div>
</body>
</html>

实现用户跟踪

在服务器端,创建一个session对象,通过这个对象就可以把用户相关的信息都保存起来。我们可以给每个session对象分配一个全局唯一的标识符来识别session对象,我们姑且称之为sessionid,每次客户端发起请求时,只要携带上这个sessionid,就有办法找到与之对应的session对象,从而实现在两次请求之间记住该用户的信息,也就是我们之前说的用户跟踪。

要让客户端记住并在每次请求时带上sessionid又有以下几种做法:

  1. URL重写。所谓URL重写就是在URL中携带sessionid,例如:http://www.example.com/index.html?sessionid=123456,服务器通过获取sessionid参数的值来取到与之对应的session对象。
  2. 隐藏域(隐式表单域)。在提交表单的时候,可以通过在表单中设置隐藏域向服务器发送额外的数据。例如:<input type=“hidden” name=“sessionid” value=“123456”>。
  3. 本地存储。现在的浏览器都支持多种本地存储方案,包括:cookie、localStorage、sessionStorage、IndexedDB等。在这些方案中,cookie是历史最为悠久也是被诟病得最多的一种方案,也是我们接下来首先为大家讲解的一种方案。

Django框架对session的支持

在创建Django项目时,默认的配置文件settings.py文件中已经激活了一个名为SessionMiddleware的中间件,因为这个中间件的存在,我们可以直接通过请求对象的session属性来操作会话对象。与此同时,SessionMiddleware中间件还封装了对cookie的操作,在cookie中保存了sessionid。

在默认情况下,Django将session的数据序列化后保存在关系型数据库中,在后面的章节中将session保存到缓存服务中以提升系统的性能。

实现用户登录验证

生成验证码随机数

首先,我们在刚才的polls/utils.py文件中编写生成随机验证码的函数gen_random_code,内容如下所示。

import random

ALL_CHARS = '23456789abcdefghjklmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'


def gen_random_code(length=4):
    return ''.join(random.choices(ALL_CHARS, k=length))

添加字体与验证码图片类Captcha

创建polls/fonts目录,放置arialbd.ttf

在polls目录下编写生成验证码图片的类Captcha。

"""
图片验证码
"""
import os
import random
from io import BytesIO

from PIL import Image
from PIL import ImageFilter
from PIL.ImageDraw import Draw
from PIL.ImageFont import truetype


class Bezier:
    """贝塞尔曲线"""

    def __init__(self):
        self.tsequence = tuple([t / 20.0 for t in range(21)])
        self.beziers = {}

    def make_bezier(self, n):
        """绘制贝塞尔曲线"""
        try:
            return self.beziers[n]
        except KeyError:
            combinations = pascal_row(n - 1)
            result = []
            for t in self.tsequence:
                tpowers = (t ** i for i in range(n))
                upowers = ((1 - t) ** i for i in range(n - 1, -1, -1))
                coefs = [c * a * b for c, a, b in zip(combinations,
                                                      tpowers, upowers)]
                result.append(coefs)
            self.beziers[n] = result
            return result


class Captcha:
    """验证码"""

    def __init__(self, width, height, fonts=None, color=None):
        self._image = None
        self._fonts = fonts if fonts else \
            [os.path.join(os.path.dirname(__file__), 'fonts', font)
             for font in ['arialbd.ttf']]# arialbd.ttf
             #for font in ['Arial.ttf', 'Georgia.ttf', 'Action.ttf']]# arialbd.ttf
        self._color = color if color else random_color(0, 200, random.randint(220, 255))
        self._width, self._height = width, height

    @classmethod
    def instance(cls, width=200, height=75):
        """用于获取Captcha对象的类方法"""
        prop_name = f'_instance_{width}_{height}'
        if not hasattr(cls, prop_name):
            setattr(cls, prop_name, cls(width, height))
        return getattr(cls, prop_name)

    def _background(self):
        """绘制背景"""
        Draw(self._image).rectangle([(0, 0), self._image.size],
                                    fill=random_color(230, 255))

    def _smooth(self):
        """平滑图像"""
        return self._image.filter(ImageFilter.SMOOTH)

    def _curve(self, width=4, number=6, color=None):
        """绘制曲线"""
        dx, height = self._image.size
        dx /= number
        path = [(dx * i, random.randint(0, height))
                for i in range(1, number)]
        bcoefs = Bezier().make_bezier(number - 1)
        points = []
        for coefs in bcoefs:
            points.append(tuple(sum([coef * p for coef, p in zip(coefs, ps)])
                                for ps in zip(*path)))
        Draw(self._image).line(points, fill=color if color else self._color, width=width)

    def _noise(self, number=50, level=2, color=None):
        """绘制扰码"""
        width, height = self._image.size
        dx, dy = width / 10, height / 10
        width, height = width - dx, height - dy
        draw = Draw(self._image)
        for i in range(number):
            x = int(random.uniform(dx, width))
            y = int(random.uniform(dy, height))
            draw.line(((x, y), (x + level, y)),
                      fill=color if color else self._color, width=level)

    def _text(self, captcha_text, fonts, font_sizes=None, drawings=None, squeeze_factor=0.75, color=None):
        """绘制文本"""
        color = color if color else self._color
        fonts = tuple([truetype(name, size)
                       for name in fonts
                       for size in font_sizes or (65, 70, 75)])
        draw = Draw(self._image)
        char_images = []
        for c in captcha_text:
            font = random.choice(fonts)
            c_width, c_height = draw.textsize(c, font=font)
            char_image = Image.new('RGB', (c_width, c_height), (0, 0, 0))
            char_draw = Draw(char_image)
            char_draw.text((0, 0), c, font=font, fill=color)
            char_image = char_image.crop(char_image.getbbox())
            for drawing in drawings:
                d = getattr(self, drawing)
                char_image = d(char_image)
            char_images.append(char_image)
        width, height = self._image.size
        offset = int((width - sum(int(i.size[0] * squeeze_factor)
                                  for i in char_images[:-1]) -
                      char_images[-1].size[0]) / 2)
        for char_image in char_images:
            c_width, c_height = char_image.size
            mask = char_image.convert('L').point(lambda i: i * 1.97)
            self._image.paste(char_image,
                              (offset, int((height - c_height) / 2)),
                              mask)
            offset += int(c_width * squeeze_factor)

    @staticmethod
    def _warp(image, dx_factor=0.3, dy_factor=0.3):
        """图像扭曲"""
        width, height = image.size
        dx = width * dx_factor
        dy = height * dy_factor
        x1 = int(random.uniform(-dx, dx))
        y1 = int(random.uniform(-dy, dy))
        x2 = int(random.uniform(-dx, dx))
        y2 = int(random.uniform(-dy, dy))
        warp_image = Image.new(
            'RGB',
            (width + abs(x1) + abs(x2), height + abs(y1) + abs(y2)))
        warp_image.paste(image, (abs(x1), abs(y1)))
        width2, height2 = warp_image.size
        return warp_image.transform(
            (width, height),
            Image.QUAD,
            (x1, y1, -x1, height2 - y2, width2 + x2, height2 + y2, width2 - x2, -y1))

    @staticmethod
    def _offset(image, dx_factor=0.1, dy_factor=0.2):
        """图像偏移"""
        width, height = image.size
        dx = int(random.random() * width * dx_factor)
        dy = int(random.random() * height * dy_factor)
        offset_image = Image.new('RGB', (width + dx, height + dy))
        offset_image.paste(image, (dx, dy))
        return offset_image

    @staticmethod
    def _rotate(image, angle=25):
        """图像旋转"""
        return image.rotate(random.uniform(-angle, angle),
                            Image.BILINEAR, expand=1)

    def generate(self, captcha_text='', fmt='PNG'):
        """生成验证码(文字和图片)
        :param captcha_text: 验证码文字
        :param fmt: 生成的验证码图片格式
        :return: 验证码图片的二进制数据
        """
        self._image = Image.new('RGB', (self._width, self._height), (255, 255, 255))
        self._background()
        self._text(captcha_text, self._fonts,
                   drawings=['_warp', '_rotate', '_offset'])
        self._curve()
        self._noise()
        self._smooth()
        image_bytes = BytesIO()
        self._image.save(image_bytes, format=fmt)
        return image_bytes.getvalue()


def pascal_row(n=0):
    """生成毕达哥拉斯三角形(杨辉三角)"""
    result = [1]
    x, numerator = 1, n
    for denominator in range(1, n // 2 + 1):
        x *= numerator
        x /= denominator
        result.append(x)
        numerator -= 1
    if n & 1 == 0:
        result.extend(reversed(result[:-1]))
    else:
        result.extend(reversed(result))
    return result


def random_color(start=0, end=255, opacity=255):
    """获得随机颜色"""
    red = random.randint(start, end)
    green = random.randint(start, end)
    blue = random.randint(start, end)
    if opacity is None:
        return red, green, blue
    return red, green, blue, opacity

说明:上面的代码中用到了三个字体文件,字体文件位于polls/fonts目录下,大家可以自行添加字体文件,但是需要注意字体文件的文件名跟上面代码的第45行保持一致。

修改polls/views.py文件处理验证码请求与修改登录请求

接下来,我们先完成提供验证码的视图函数。

from polls.Captcha import Captcha
from polls.utils import gen_random_code



def get_captcha(request: HttpRequest) -> HttpResponse:
    """验证码"""
    captcha_text = gen_random_code()
    request.session['captcha'] = captcha_text
    image_data = Captcha.instance().generate(captcha_text)
    return HttpResponse(image_data, content_type='image/png')

注意上面代码中的第4行,我们将随机生成的验证码字符串保存到session中,稍后用户登录时,我们要将保存在session中的验证码字符串和用户输入的验证码字符串进行比对,如果用户输入了正确的验证码才能够执行后续的登录流程,代码如下所示。

from polls.Captcha import Captcha
from polls.utils import gen_md5_digest, gen_random_code

from polls.models import User


def login(request: HttpRequest) -> HttpResponse:
    hint = ''
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username and password:
            password = gen_md5_digest(password)
            user = User.objects.filter(username=username, password=password).first()
            if user:
                request.session['userid'] = user.no
                request.session['username'] = user.username
                return redirect('/')
            else:
                hint = '用户名或密码错误'
        else:
            hint = '请输入有效的用户名和密码'
    return render(request, 'login.html', {'hint': hint})

说明:上面的代码没有对用户名和密码没有进行验证,实际项目中建议使用正则表达式验证用户输入信息,否则有可能将无效的数据交给数据库进行处理或者造成其他安全方面的隐患。

上面的代码中,我们设定了登录成功后会在session中保存用户的编号(userid)和用户名(username),页面会重定向到首页。

修改polls/views.py文件,logout函数

如果用户没有登录,页面会显示登录和注册的超链接;而用户登录成功后,页面上会显示用户名和注销的链接,注销链接对应的视图函数如下所示,URL的映射与之前讲过的类似,不再赘述。

def logout(request):
    """注销"""
    request.session.flush()
    return redirect('/')

上面的代码通过session对象flush方法来销毁session,一方面清除了服务器上session对象保存的用户数据,一方面将保存在浏览器cookie中的sessionid删除掉,稍后我们会对如何读写cookie的操作加以说明。

我们可以通过项目使用的数据库中名为django_session 的表来找到所有的session,该表的结构如下所示:
在这里插入图片描述

其中,第1列就是浏览器cookie中保存的sessionid;第2列是经过BASE64编码后的session中的数据。

修改polls/views.py文件praise_or_criticize函数,限制只有登录的用户才能投票

接下来,我们就可以限制只有登录用户才能为老师投票,修改后的praise_or_criticize函数如下所示,我们通过从request.session中获取userid来判定用户是否登录。

def praise_or_criticize(request: HttpRequest) -> HttpResponse:
    if request.session.get('userid'):
        try:
            tno = int(request.GET.get('tno'))
            teacher = Teacher.objects.get(no=tno)
            if request.path.startswith('/praise/'):
                teacher.good_count += 1
                count = teacher.good_count
            else:
                teacher.bad_count += 1
                count = teacher.bad_count
            teacher.save()
            data = {'code': 20000, 'mesg': '投票成功', 'count': count}
        except (ValueError, Teacher.DoesNotExist):
            data = {'code': 20001, 'mesg': '投票失败'}
    else:
        data = {'code': 20002, 'mesg': '请先登录'}
    return JsonResponse(data)

修改teachers.html,如果没有登录,跳转到登录页

当然,在修改了视图函数后,teachers.html也需要进行调整,用户如果没有登录,就将用户引导至登录页,登录成功再返回到投票页,此处不再赘述。

window.location.href='http://127.0.0.1:8000/login/';

实现用户注册

修改polls/views.py添加register函数

def register(request: HttpRequest) -> HttpResponse:
    from datetime import datetime
    reg_date = datetime.now()

    hint = ''
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        password = gen_md5_digest(password)
        tel = request.POST.get('tel')
        user = User.objects.filter(username=username).first()
        if user:
            hint = '用户名已存在'
            return render(request, 'register.html', {'hint': hint})

        print(reg_date,"<--------reg_date")
        user = User(username=username,password=password,tel=tel,reg_date=reg_date,)
        user.save()

        
        user = User.objects.filter(username=username).first()
        if user:
            hint="用户 {} 注册成功".format(username)
            return redirect('/')
        else:
            hint = '请重新注册'
            return render(request, 'register.html', {'hint': hint})

    return render(request, 'register.html', {'hint': hint})

修改urls.py文件,添加注册路由

path('register/', polls_views.register),# 注册 本部分新增 前面如果已经添加九不要重复添加

创建templates/register.html

templates/register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
    <style>
        #container {
            width: 520px;
            margin: 10px auto;
        }
        .input {
            margin: 20px 0;
            width: 460px;
            height: 40px;
        }
        .input>label {
            display: inline-block;
            width: 140px;
            text-align: right;
        }
        .input>img {
            width: 150px;
            vertical-align: middle;
        }
        input[name=captcha] {
            vertical-align: middle;
        }
        form+div {
            margin-top: 20px;
        }
        form+div>a {
            text-decoration: none;
            color: darkcyan;
            font-size: 1.2em;
        }
        .button {
            width: 500px;
            text-align: center;
            margin-top: 20px;
        }
        .hint {
            color: red;
            font-size: 12px;
        }
    </style>
</head>
<body>
    <div id="container">
        <h1>用户注册</h1>
        <hr>
        <p class="hint">{{ hint }}</p>
        <form action="/register/" method="post">
            {% csrf_token %}
            <fieldset>
                <legend>用户信息</legend>
                <div class="input">
                    <label>用户名:</label>
                    <input type="text" name="username">
                </div>
                <div class="input">
                    <label>密码:</label>
                    <input type="password" name="password">
                </div>
                <div class="input">
                    <label>手机:</label>
                    <input type="text" name="tel">
                </div>
                <div class="input">
                    <label>验证码:</label>
                    <input type="text" name="captcha">
                    <img id="code" src="/captcha/" alt="" width="150" height="40">
                </div>
            </fieldset>
            <div class="button">
                <input type="submit" value="注册">
                <input type="reset" value="重置">
            </div>
        </form>
        <div>
            <a href="/">返回首页</a>
            <a href="/login/">登录</a>
        </div>
    </div>
</body>
</html>

总结

本文主要是Django系列博客。本文是Django静态资源与Ajax请求示例。
1.创建静态资源目录
2.配置settings.py文件
3.修改urls.py文件
4.修改views.py文件
5.修改teachers.html文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/401593.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据仓库相关概念的解释

数据仓库相关概念的解释 文章目录数据仓库相关概念的解释1 ETL是什么&#xff1f;ETL体系结构2 数据流向何为数仓DW3 ODS 是什么&#xff1f;4 数据仓库层DWDWD 明细层DWD 轻度汇总层&#xff08;MID或DWB&#xff0c;data warehouse basis&#xff09;DWS 主题层&#xff08;D…

MySQL8读写分离集群

文章目录前言MySQL读写分离原理搭建MySQL读写分离集群MySQL8.0之前MySQL8.0之后后记前言 上一期介绍并实现了MySQL的主从复制&#xff0c;由于主从复制架构仅仅能解决数据冗余备份的问题&#xff0c;从节点不对外提供服务&#xff0c;依然存在单节点的高并发问题 所以在主从复…

【Vue】10分钟带你读懂Vue的过滤器

一、什么是过滤器&#xff1f;过滤器提供给我们的一种数据处理方式。过滤器功能不是必须要使用的&#xff0c;因为它所实现的功能也能用计算属性或者函数调用的方式来实现。Vue.js 允许你自定义过滤器&#xff0c;可被用于一些常见的文本格式化。二、过滤器声明与使用过滤器应该…

重构SeleniumeDownloader底层浏览器驱动

一、解决bug&#xff1a;Selenium with PhantomJS&#xff0c;重构SeleniumeDownloader底层浏览器驱动 0、小背景&#xff1a; 想爬取外网steam的数据&#xff0c;但是steam官网在海外&#xff0c;加上steam处于反爬考虑&#xff0c;对于异步数据-json数据进行处理&#xff0…

三天吃透RabbitMQ面试八股文

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…

51驱动NRF24L01通信,NRF24L01与TTL转NRF24L01模块通信

51驱动NRF24L01通信&#xff0c;NRF24L01与TTL转NRF24L01模块通信NRF24L01一、简介二、引脚功能描述程序设计一、对 24L01 的程序编程的基本思路如下&#xff1a;二、Tx 与 Rx 的配置过程1、Tx 模式初始化过程&#xff1a;2、Rx 模式初始化过程&#xff1a;三、基本程序函数通信…

五分钟了解支付、交易、清算、银行等专业名词的含义?

五分钟了解支付、交易、清算、银行等专业名词的含义&#xff1f;1. 支付类名词01 支付应用02 支付场景03 交易类型04 支付类型&#xff08;按通道类型&#xff09;05 支付类型&#xff08;按业务双方类型&#xff09;06 支付方式07 支付产品08 收银台类型09 支付通道10 通道类型…

LeetCode-416. 分割等和子集

目录题目分析回溯法动态规划动态规划(压缩)题目来源 416. 分割等和子集 题目分析 这道题目是要找是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 那么只要找到集合里能够出现 sum / 2 的子集总和&#xff0c;就算是可以分割成两个相同元素和子集了…

使用 ONLYOFFICE 宏借助 ChatGPT 生成文章

AI 技术在过去几年中得到了显著提升&#xff0c;同时也成为了我们日常生活中必不可少的一部分。现在&#xff0c;我们会将这种高科技功能纳入到文档撰写过程。在本文中&#xff0c;我们将展示如何构建一个宏来使用 ChatGPT API 生成文章。 关于 ChatGPT ChatGPT 是由 OpenAI 开…

html2canvas和jspdf导出pdf,每个页面模块占一页,在pdf中垂直居中显示

需求&#xff1a;html页面转换pdf&#xff0c;页面有多个模块&#xff0c;页面中有文本、echarts、表格等模块&#xff0c;一个模块占一页&#xff0c;因为模块高度不够&#xff0c;所以需要垂直居中 通过html2canvas和jspdf实现&#xff0c;html2canvas用于将页面元素生成canv…

以java编写员工管理系统(测试过 无问题)

一、系统结果的部分展示 二、题目以及相关要求 三、组成 1.该系统由 Employee 类 、commonEmployee类、Testemd类和managerEmployee类组成 2.Employee实现的代码 public class Employee {private String id;private String name;private String job;private int holiday…

弱监督参考图像分割:Learning From Box Annotations for Referring Image Segmentation论文阅读笔记

弱监督参考图像分割&#xff1a;Learning From Box Annotations for Referring Image Segmentation论文阅读笔记一、Abstract二、引言三、相关工作A、全监督参考图像分割B、基于 Box 的实例分割C、带有噪声标签的学习四、提出的方法A、概述B、伪标签生成目标轮廓预测Proposal 选…

exe打包工具:advanced installer介绍(一)

前言近年来&#xff0c;web服务逐渐走向云端部署浏览器化、去APP化&#xff0c;然而exe安装仍有着举足轻重的地位&#xff0c;其好处不言而喻&#xff0c;拿到exe安装包后&#xff0c;基本就可以傻瓜安装和操作了&#xff0c;十分便捷。业务场景通过各种IDE/QT/C#/Java等开发工…

项目的生命周期与成本、风险、变更的关系

成本与人力投入水平 项目成本的投入在初始阶段逐渐增加&#xff0c;在执行的中间阶段达到顶峰&#xff0c;在项目收尾阶段逐渐下降。成本的投入趋势如下图所示&#xff1a; 初始阶段&#xff1a;从人力成本来看&#xff0c;信息系统开发团队在前期制定项目管理章程及项目管理…

PCB焊盘设计基本原则

SMT的组装质量与PCB焊盘设计有直接的关系&#xff0c;焊盘的大小比例十分重要。如果PCB焊盘设计正确&#xff0c;贴装时少量的歪斜可以再次回流焊纠正(称为自定位或自校正效应)&#xff0c;相反&#xff0c;如果PCB焊盘设计不正确&#xff0c;即使贴装位置十分准确&#xff0c;…

图像金字塔,原理、实现及应用

什么是图像金字塔 图像金字塔是对图像的一种多尺度表达&#xff0c;将各个尺度的图像按照分辨率从小到大&#xff0c;依次从上到下排列&#xff0c;就会形成类似金字塔的结构&#xff0c;因此称为图像金字塔。 常见的图像金字塔有两类&#xff0c;一种是高斯金字塔&#xff0…

为什么越来越多的人开始学习大数据

因为根据国内的发展形势&#xff0c;大数据未来的发展前景会非常好&#xff0c;前景好需求高&#xff0c;自然会吸引越来越多的人进入大数据行业 我国市场环境处于急需大数据人才但人才不足的阶段&#xff0c;所以未来大数据领域会有很多的就业机遇。 2022年春季&#xff0c;…

camunda流程引擎基本使用(笔记)

文章目录一、camunda基础1.1 安装与部署流程引擎1.2 流程引擎结构1.3 流程引擎的基本使用1.3.1 创建一个BPMN Diagram1.3.2 实现一个外部工作者1.3.3 部署流程1.3.4 创建一个流程实例并消费1.3.5 向流程中添加用户任务1.3.6 添加网关1.3.7 业务规则二、Java 集成流程引擎2.1 为…

酷开科技大数据揭秘!酷开系统中的千屏千面究竟指的是什么?

互联网行业的快速发展&#xff0c;给我们带来了极大的便利。回顾整个互联网行业的发展历程&#xff0c;从PC时代到移动互联网时代&#xff0c;从移动互联网时代到物联网时代&#xff0c;现在又即将从物联网时代迈入人工智能时代。这些飞速发展的背后&#xff0c;其实是对数据利…

ICG-alkyne,吲哚菁绿-炔基结构式,实验室科研试剂,CAS号:1622335-41-4

ICG-alkyne,吲哚菁绿-炔基 中文名称&#xff1a;吲哚菁绿-炔基 CAS号&#xff1a;1622335-41-4 英文名称&#xff1a;ICG-alkyne 英文别名&#xff1a;ICG-alk 性状&#xff1a;绿色粉末 化学式&#xff1a;C48H53N3O4S 分子量&#xff1a;768.03 溶剂&#xff1a;溶于…