2024--Django平台开发-Django知识点(七)

频率超高的问题

  1. Redis的问题
  2. 虚拟环境
  3. mysqlcient和pymysql
  4. 短信服务,一期用的是腾讯云短信

虚拟环境

可以用来创建虚拟环境的:

  1. virtualenv这个模块,简单易上手,推荐

  2. 小白不建议,conda,如果大家用这个,简单用的,就miniconda比较简洁,Anaconda比较全。都有一个包管理器叫做conda,相当于pip,你可以用conda命令来管理虚拟环境,它比virtualenv要好用。

    参考:https://www.cnblogs.com/Neeo/articles/9574705.html#%E8%99%9A%E6%8B%9F%E7%8E%AF%E5%A2%83%E7%AE%A1%E7%90%86

    miniconda官网:https://docs.conda.io/en/latest/miniconda.html#windows-installers

关于这个virtualenv的坑1,中文路径

坑:千万不要将你的虚拟环境,创建在包含中文或者其他特殊字符的目录下。

在这里插入图片描述

包含中文路径下,创建并使用虚拟环境,有问题:

在这里插入图片描述

在不包含中文路径下,创建并使用虚拟环境,没问题。
在这里插入图片描述

建议:

激活虚拟环境之后,下载模块之前,都要去pip -V确认下虚拟环境激活没有。

关于这个virtualenv的坑2,创建的虚拟环境无法使用

现象:使用虚拟环境运行Django项目,提示当前虚拟环境中没有找到Django,即没有找到Django这个模块,但Django模块是下载好的。

就是死活读不到。

如何解决。

第一步,先将虚拟环境中的项目依赖导出到requirements.txt文件中。

(venv) D:\day7>pip freeze > requiements.txt

(venv) D:\day7>

第二步,从本地将你的虚拟环境(文件夹)删除。

第三步,从新创建一个虚拟环境,

D:\day7>virtualenv  venv

第四步,激活虚拟环境,并且从requirements.txt文件中,将项目依赖下载到当前虚拟环境中。

在这里插入图片描述

第五步,就是在pycharm中,重新配置解释器,应用上虚拟环境。

关于这个virtualenv的坑3,虚拟环境的名字有问题

通常创建的虚拟环境名字叫做env或者venv,但极个别的情况,venv这个虚拟环境名字不能用,现象,也是虚拟环境无法激活。

解决办法,删掉这个虚拟环境,再重新创建的时候,换个别的名字,比如说abc

额外的卸载重装python解释器

如果是Windows,不要直接删,而是从系统设置中去卸载:

在这里插入图片描述

关于pycharm无法创建Django项目

如果你是这样创建Django项目的:

在这里插入图片描述

但是,创建时,遇到error错误了,

在这里插入图片描述

除了这个问题,还有个就是,这种创建Django项目的方式,默认下载的Django是最新版的。如果你的项目必须要求低版本的,或者其他指定版本,这种方式,就不太好了。

针对以上报错问题,还有无法选择版本的问题,我通常是建议,不要用pycharm提供的快速创建Django项目的功能了。

我们自己来,在终端中执行:

# 1. 选择项目要创建在哪个目录下,就在哪个目录下打开终端,手动创建项目名
D:\day7>mkdir demo

# 2. 切换到项目名中,也就是项目根目录下
D:\day7>cd demo

# 3. 创建该项目所需的虚拟环境
D:\day7\demo>virtualenv venv
created virtual environment CPython3.10.9.final.0-64 in 364ms
  creator CPython3Windows(dest=D:\day7\demo\venv, clear=False, no_vcs_ignore=False, global=False)
  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=C:\Users\12061\AppData\Local\pypa\virtualenv)
    added seed packages: pip==23.1.2, setuptools==68.0.0, wheel==0.40.0
  activators BashActivator,BatchActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator

# 4. 激活虚拟环境
D:\day7\demo>.\venv\Scripts\activate

# 5. 确认是否激活虚拟环境
(venv) D:\day7\demo>pip -V
pip 23.1.2 from D:\day7\demo\venv\lib\site-packages\pip (python 3.10)

# 6. 安装指定版本的Django
(venv) D:\day7\demo>pip install django==4.2.3
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting django==4.2.3
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/d4/83/227ebf197e413f3599cea96dddc7d6b8ff220310cc5b40dd0f1a15e5a9d1/Django-4.2.3-py3-none-any.whl (8.0 MB)
Collecting asgiref<4,>=3.6.0 (from django==4.2.3)
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/9b/80/b9051a4a07ad231558fcd8ffc89232711b4e618c15cb7a392a17384bbeef/asgiref-3.7.2-py3-none-any.whl (24 kB)
Collecting sqlparse>=0.3.1 (from django==4.2.3)
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/98/5a/66d7c9305baa9f11857f247d4ba761402cea75db6058ff850ed7128957b7/sqlparse-0.4.4-py3-none-any.whl (41 kB)
Collecting tzdata (from django==4.2.3)
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/d5/fb/a79efcab32b8a1f1ddca7f35109a50e4a80d42ac1c9187ab46522b2407d7/tzdata-2023.3-py2.py3-none-any.whl (341 kB)
Collecting typing-extensions>=4 (from asgiref<4,>=3.6.0->django==4.2.3)
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/ec/6b/63cc3df74987c36fe26157ee12e09e8f9db4de771e0f3404263117e75b95/typing_extensions-4.7.1-py3-none-any.whl (33 kB)
Installing collected packages: tzdata, typing-extensions, sqlparse, asgiref, django
Successfully installed asgiref-3.7.2 django-4.2.3 sqlparse-0.4.4 typing-extensions-4.7.1 tzdata-2023.3

[notice] A new release of pip is available: 23.1.2 -> 23.2.1
[notice] To update, run: python.exe -m pip install --upgrade pip

# 7. 确认的Django是否安装成功
(venv) D:\day7\demo>django-admin --version
4.2.3

# 8. 创建项目, 下面命令中的点,表示创建好的项目文件都保存在项目根目录下
(venv) D:\day7\demo>django-admin startproject demo .

# 9. 创建app
# 补充,创建app的两种方式
# 1. django-admin startapp api    # django-amdin命令创建
# 2. 在项目根目录下,执行python manage.py startapp app01   # 这是第二种方式
# 推荐哪个?你可以先使用第二种,万一出了问题,再使用第一中尝试。
(venv) D:\day7\demo>django-admin startapp api

# 10. 测试项目能否正常运行
(venv) D:\day7\demo>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
July 30, 2023 - 09:49:01
Django version 4.2.3, using settings 'demo.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

[30/Jul/2023 09:49:03] "GET / HTTP/1.1" 200 10664
Not Found: /favicon.ico
[30/Jul/2023 09:49:03] "GET /favicon.ico HTTP/1.1" 404 2108

在这里插入图片描述

  1. pycharm从本地打开项目,然后添加当前虚拟环境到pycharm的解释器配置中了。
    在这里插入图片描述

  2. 检查Django项目的配置是否正确。

在这里插入图片描述

在这里插入图片描述

  1. 检查settings配置,手动注册app。

在这里插入图片描述

  1. 关于模板文件夹的配置,手动创建模板文件夹。

在这里插入图片描述

在这里插入图片描述

关于Django纯净版的问题

首先Django纯净版的本身没问题,但大家老是会遇到相关的问题。

我不建议初学者用这个Django纯净版,因为你不知道有些模块或者三方框架内部有用到我们注释掉的这些组件,导致运行时,可能出现一些报错,你也解决不了。

建议所有组件都用上,功能都有,我们可以不用,但如果有三方组件用到了,它内部就不报错了。

关于mysqlclient和pymysql的相爱相杀

超频问题:我项目中用mysql,我用哪个模块。

建议:先使用mysqlclient

pip install mysqlclient

如果下载成功,不报错,其它的一点都不用配置,项目就能正常使用了。

如果下载过程中,出现报错,或者使用过程中,出现mysql的连接问题,排查了settings配置没问题之后,我们不纠结,也不排查mysqlclient的错误。而是直接改用pymysql。

pip install pymysql

pymysql和mysqlclient的使用没有任何区别,就是pymysql比mysqlclient多了一个配置。

那就是pymysql在下载之后,需要你在settings.py同级目录下的__init__.py文件中,添加下面两行代码,就ok了。

import pymysql
pymysql.install_as_MySQLdb()

项目中集成云短信

原来,推荐使用腾讯云短信。但最近,发现腾讯云短信的功能开通,大家可能会卡在签名这里,因为要认证。比如说,企业资质,我们个人开发学习阶段,不容易开通, 导致用不了。

这是咱们一期项目中,同学遇到的关于短信的主要问题。

我个人建议:如果你自己学习阶段,去开通腾讯云短信服务,卡在某个环节,进行不下去了。那么你没比较纠结。可以专用其它厂家提供的短信服务。

  • 云通讯。
  • 互亿无限。

其实用哪个厂家的短信服务,没关系,你会发现,其实都没几行代码。

参考:https://www.cnblogs.com/Neeo/articles/16672659.html

腾讯云短信

  1. 下载模块
pip install --upgrade tencentcloud-sdk-python
  1. 创建好应用,并获取应用ID,打开连接:https://console.cloud.tencent.com/smsv2/app-manage/detail/1400793930,遇到登录,就微信扫码登录。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这里插入图片描述
在这里插入图片描述

  1. 准备好秘钥,用于获取SecretId和SecretKey,秘钥管理:https://console.cloud.tencent.com/cam/capi
    在这里插入图片描述

在这里插入图片描述

  1. 签名,准备好签名,这一步有点麻烦,因为需要审核。好多学生卡在这里了。连接:https://console.cloud.tencent.com/smsv2/csms-sign

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 准备好模板,比如| 验证码为:{1},您正在登录,若非本人操作,请勿泄露。其中的{1}就是我们将来要自定义

    • 模板管理:https://console.cloud.tencent.com/smsv2/csms-template

    在这里插入图片描述

在这里插入图片描述

  1. 下面是我整理好的腾讯云短信发送的示例,可以拷贝到你的项目中进行使用。
# -*- coding = utf-8 -*-
import random
import string
from tencentcloud.common import credential
from tencentcloud.sms.v20210111 import sms_client, models

# 注意,下面写法是固定的,你只需要按照上面给的链接中,获取对应的值,来修改即可

# 下面这俩值从这个页面中获取:https://console.cloud.tencent.com/cam/capi
SecretId = "AKIDQdZFemCGdlcCo9sQ9sKkwriIq776rUu4"
SecretKey = "FEvLZWBZgX7JFzRcW8VbgU7UZOvCuc3H"


cred = credential.Credential(
    secret_id=SecretId,    # 注意必须以关键字的形式传参
    secret_key=SecretKey   # 注意必须以关键字的形式传参
)  # secretId secretKey
client = sms_client.SmsClient(cred, "ap-guangzhou")  # 固定写法无需变动

req = models.SendSmsRequest()  # 固定写法无需变动

# 应用id,注意这个是你创建的应用id,是这个链接:https://console.cloud.tencent.com/smsv2/app-manage 中的应用ID,
# 不是密钥页面的APPID,既不是这个页面的ID:https://console.cloud.tencent.com/cam/capi
req.SmsSdkAppId = "1400793930"

# 模板相关的值从这个页面中获取:https://console.cloud.tencent.com/smsv2/csms-template
req.SignName = "张开与老虎"   # 模板签名
req.TemplateId = "575000"   # 模板id(可以有多套模板进行更换,这样发送的短信内容就可以改变了)

# 下面就是结合模板填写具体要发送的验证码和手机号了
code = ''.join(random.sample(string.digits, 6))  # 随机生成的6位验证码
print(code)  # 671093
req.TemplateParamSet = [code]  # 发送的6位验证码
req.PhoneNumberSet = ["+8618211101742"]   # 目标手机号

resp = client.SendSms(req)  # 发送
print(resp)
"""
{
	"SendStatusSet": [
		{
			"SerialNo": "2433:326237685316756579077330174", 
			"PhoneNumber": "+8618211101742", 
			"Fee": 1, "SessionContext": "", 
			"Code": "Ok", "Message": "send success", "IsoCode": "CN"
			}
	], 
	"RequestId": "25147cb8-9abd-4100-bdbb-bb61cd3c2828"
}

# 你的手机号,应该接收到了短信:
【张开与老虎】验证码为:671093,您正在登录,若非本人操作,请勿泄露。
"""

优化一版:

# -*- coding = utf-8 -*-
import random
import string
from tencentcloud.common import credential
from tencentcloud.sms.v20210111 import sms_client, models

# 注意,下面写法是固定的,你只需要按照上面给的链接中,获取对应的值,来修改即可

# 下面这俩值从这个页面中获取:https://console.cloud.tencent.com/cam/capi
SecretId = "AKIDQdZFemCGdlcCo9sQ9sKkwriIq776rUu4"
SecretKey = "FEvLZWBZgX7JFzRcW8VbgU7UZOvCuc3H"

def getcode(num):
    # 下面就是结合模板填写具体要发送的验证码和手机号了
    code = ''.join(random.sample(string.digits, num))  # 根据需要随机生成的指定位数的验证码
    print(code)  # 671093
    return code

def send_sms(phone, temp_id):
    cred = credential.Credential(
        secret_id=SecretId,    # 注意必须以关键字的形式传参
        secret_key=SecretKey   # 注意必须以关键字的形式传参
    )  # secretId secretKey
    client = sms_client.SmsClient(cred, "ap-guangzhou")  # 固定写法无需变动

    req = models.SendSmsRequest()  # 固定写法无需变动

    # 应用id,注意这个是你创建的应用id,是这个链接:https://console.cloud.tencent.com/smsv2/app-manage 中的应用ID,
    # 不是密钥页面的APPID,既不是这个页面的ID:https://console.cloud.tencent.com/cam/capi
    req.SmsSdkAppId = "1400793930"

    # 模板相关的值从这个页面中获取:https://console.cloud.tencent.com/smsv2/csms-template
    req.SignName = "张开与老虎"   # 模板签名
    req.TemplateId = temp_id   # 模板id(可以有多套模板进行更换,这样发送的短信内容就可以改变了)


    req.TemplateParamSet = [getcode(num=4)]  # 发送的4位验证码
    req.PhoneNumberSet = [f"+86{phone}"]   # 目标手机号

    resp = client.SendSms(req)  # 发送
    # print(333, resp.SendStatusSet[0].__dict__)
    # print(333, resp.SendStatusSet[0].__dict__['_Code'])
    # 我这里发送太频繁了,导致下面代码没法测了,后续你们自己可以根据打印内容进行测试,主要就是判断发送成功还是失败
    if resp.SendStatusSet[0].__dict__['_Code'] == 'OK':
        # 短信发送成功
        print(11)
        return True
    else:
        # 短信发送失败
        print(22)
        return False
    """
    {
        "SendStatusSet": [
            {
                "SerialNo": "2433:326237685316756579077330174", 
                "PhoneNumber": "+8618211101742", 
                "Fee": 1, "SessionContext": "", 
                "Code": "Ok", "Message": "send success", "IsoCode": "CN"
                }
        ], 
        "RequestId": "25147cb8-9abd-4100-bdbb-bb61cd3c2828"
    }

    # 你的手机号,应该接收到了短信:
    【张开与老虎】验证码为:671093,您正在登录,若非本人操作,请勿泄露。

    # 更换不同的模板id,其它代码可以不变,就能动态的发送不同的短信
    【张开与老虎】您的动态验证码为:036724,您正在进行密码重置操作,如非本人操作,请忽略本短信!

    # 发送太频繁了,报错了
    {'_SendStatusSet': [{"SerialNo": "", "PhoneNumber": "+8618211101742", "Fee": 0, "SessionContext": "", "Code": "LimitExceeded.PhoneNumberThirtySecondLimit", "Message": "the number of SMS messages sent from a single mobile number within 30 seconds exceeds the upper limit", "IsoCode": "CN"}], '_RequestId': '7bd7d567-61a2-479a-b37c-b342f79792aa'}

    """
if __name__ == '__main__':
    res = send_sms('18211101742', "575001")  # 第二个参数是模板id,不同的模板id,可以发送不同的短信
    # 伪代码
    # if not res:
    #     return JsonResponse({"code": 1000, 'msg':"短信发送失败"})
    # # 接着往下写短信发送成功的代码

在这里插入图片描述

云通信短信(如果搞不定腾讯云短信,推荐这个)

为啥推荐,因为这个简单。

参考:https://www.cnblogs.com/Neeo/articles/16672659.html#%E5%AE%B9%E8%81%94%E4%BA%91%E7%9F%AD%E4%BF%A1

注册

https://console.yuntongxun.com/user/reg/init

在这里插入图片描述

登录

在这里插入图片描述

登录成功自动跳转到控制台主页。遇到下面的认证提示,直接点击关闭即可。

在这里插入图片描述

进入控制台首页:https://console.yuntongxun.com/member/main
在这里插入图片描述

应用

用默认的就就行了。

在这里插入图片描述

模板部分

想要自定义模板,需要充值5000块,算了吧,用默认的就行了,免费。

手机号

测试阶段,你只能向下面预留的手机号发送测试短信。

在这里插入图片描述

具体使用

在这里插入图片描述

在这里插入图片描述

常见报错

{‘statusCode’: ‘112310’, ‘statusMsg’: ‘【短信】应用未上线,模板短信接收号码外呼受限’}

Response body:  {"statusCode":"112310","statusMsg":"【短信】应用未上线,模板短信接收号码外呼受限"}

在这里插入图片描述

{‘statusCode’: ‘161125’, ‘statusMsg’: ‘请输入1到4位的数字’}

在这里插入图片描述

# -*- coding = utf-8 -*-
import random
import json
from ronglian_sms_sdk import SmsSDK

def send_sms(mobile, datas, tid=1):
    """
    发送短信
    @params tid: 模板ID,默认测试使用1
    @params mobile: 接收短信的手机号,多个手机号使用都逗号隔开
            单个号码: mobile="13312345678"
            多个号码: mobile="13312345678,13312345679,...."
    @params datas: 短信模板的参数列表
            例如短信模板为: 【云通讯】您的验证码是{1},请于{2}分钟内正确输入。
            则datas=("3456",2,)
            最终发送的短信为:【云通讯】您的验证码是{3456},请于{5}分钟内正确输入。
    """
    # 下面的配置根据你的控制台首页主账号那里获取
    RONGLIANYUN_CONFIG = {
        # 下面这三个值都可以从控制台首页的开发者主账号中进行获取
        "accId": '2c94811c87fb7ec601881e50a8ed0b39',  # 对应:ACCOUNT SID
        "accToken": '23d2e5f9f7694d9e888eb2a6848dae42',  # 对应:AUTH TOKEN
        "appId": '2c94811c87fb7ec601881e50aa210b40',  # 对应:AppID(默认)
        "reg_tid": 1,       # 注册短信验证码的模板ID,测试阶段固定为1,不需要修改
        # 下面两个根据需要修改
        "sms_expire": 120,  # 短信有效期,单位:秒(s),这是真正的超时时间,注意和datas=("3456",2,)中第二个参数进行换算,保持一致
        "sms_interval": 60,  # 短信发送的冷却时间,单位:秒(s)
    }
    # 不需要修改
    sdk = SmsSDK(RONGLIANYUN_CONFIG.get("accId"), RONGLIANYUN_CONFIG.get("accToken"), RONGLIANYUN_CONFIG.get("appId"))

    # 发送短信
    resp = sdk.sendMessage(str(tid), mobile, datas)
    # 拿到结果
    response = json.loads(resp)
    print(response, type(response))
    """
    Sign plaintext:  2c94811c87fb7ec601881e50a8ed0b3923d2e5f9f7694d9e888eb2a6848dae4220230524140059
    Authorization plaintext: 2c94811c87fb7ec601881e50a8ed0b39:20230524140059
    Request url:  https://app.cloopen.com:8883/2013-12-26/Accounts/2c94811c87fb7ec601881e50a8ed0b39/SMS/TemplateSMS?sig=6FB880CDC9671A41674C17DE348D300B
    Request headers:  {'Content-Type': 'application/json;charset=utf-8', 'Accept': 'application/json', 'Accept-Charset': 'UTF-8', 'Authorization': b'MmM5NDgxMWM4N2ZiN2VjNjAxODgxZTUwYThlZDBiMzk6MjAyMzA1MjQxNDAwNTk='}
    Request body:  {"to": "18211101742", "appId": "2c94811c87fb7ec601881e50aa210b40", "templateId": "1", "datas": [4653, 2]}
    Response body:  {"statusCode":"000000","templateSMS":{"smsMessageSid":"8bcc77c455084a6b8d52983e95f10977","dateCreated":"20230524140100"}}
    {'statusCode': '000000', 'templateSMS': {'smsMessageSid': '8bcc77c455084a6b8d52983e95f10977', 'dateCreated': '20230524140100'}} <class 'dict'>
    """
    # statusCode是'000000'表示发送成功
    # 手机上接收到的短信长这样
    """
    【云通讯】您使用的是云通讯短信模板,您的验证码是2491,请于2分钟内正确输入(变量仅支持1-4位数字)
    """
    return response.get("statusCode") == "000000"

def get_code(num=4):
    """
    生成指定位数的验证码,如果不传值,就默认生成4位的验证码
    :param num: 要生成几位的验证码
    :return: 生成的验证码
    """
    return random.randint(
        int('1{}'.format('0' * (num - 1))),
        int('9{}'.format('9' * (num - 1)))
    )

if __name__ == '__main__':

    res = send_sms(
        mobile='18211101742',    # 这个手机号必须在你在容联云中填写的那个3个测试手机号中
        datas=(get_code(4), 2),  # 元组的第一个参数是4位的验证码,注意免费测试的只支持发送4位的验证码,不支持6位的,第二个参数是短信模板中替换为:请于{2}分钟内正确输入
        tid=1                    # 这个模板id目前测试阶段固定为1
    )
    # 伪代码
    # if not res:
    #     return JsonResponse({"code": 1000, 'msg':"短信发送失败"})
    # # 接着往下写短信发送成功的代码

互亿无线106短信

实名认证

1. 注册

实名注册:https://user.ihuyi.com/new/register.html?e=400

在这里插入图片描述

2. 登录之后,在控制台进行个人认证

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

短信demo

首先,准备这两个值

官网链接:https://user.ihuyi.com/new/sms/overview

在这里插入图片描述

代码:

# -*- coding = utf-8 -*-
# python3
# 接口类型:互亿无线触发短信接口,支持发送验证码短信、订单通知短信等。
# 账户注册:请通过该地址开通账户https://user.ihuyi.com/new/register.html
# 注意事项:
# (1)调试期间,请用默认的模板进行测试,默认模板详见接口文档;
# (2)请使用 用户名 及 APIkey来调用接口,APIkey在会员中心可以获取;
# (3)该代码仅供接入互亿无线短信接口参考使用,客户可根据实际需要自行编写;
import json
import random
import urllib.parse
import urllib.request


def get_code(num=4):
    """
    生成指定位数的验证码,如果不传值,就默认生成4位的验证码
    :param num: 要生成几位的验证码
    :return: 生成的验证码
    """
    return random.randint(
        int('1{}'.format('0' * (num - 1))),
        int('9{}'.format('9' * (num - 1)))
    )


def send_sms(mobile, code_num=4):
    """
    发送短信验证码
    :param mobile: 你要发给谁
    :param code_num: 发送几位的验证码
    :return:
    """
    # 接口地址,咱们不需要更改
    url = 'http://106.ihuyi.com/webservice/sms.php?method=Submit'

    # 定义请求的数据
    values = {
        'account': 'C05062020',  # 这个是对应的APIID
        'password': 'aff16e275e46618efd14f21b00d6de91',  # 这个是对应的APIKEY
        'mobile': mobile,  # 发给谁
        'content': '您的验证码是:{}。请不要把验证码泄露给其他人。'.format(get_code()),
        # 没有购买套餐的,这个模板只能使用默认的,即你收到的短信长这样:【互亿无线】您的验证码是:7835。请不要把验证码泄露给其他人。
        'format': 'json',  # 不要动
    }

    # 将数据进行编码,下面代码不要动
    data = urllib.parse.urlencode(values).encode(encoding='UTF8')

    # 发起请求,下面代码不要动
    req = urllib.request.Request(url, data)
    response = urllib.request.urlopen(req)
    res = response.read()

    # 打印结果,然后你的手机应该就能接到短信了
    print(res.decode("utf8"), type(res.decode("utf8")))  # {"code":2,"msg":"提交成功","smsid":"16842079209571524017"}
    dict_res = json.loads(res.decode("utf8"))
    if dict_res.get("code", 0) == 2:
        print('短信发送成功,请留意手机')
        return True
    else:
        print('发送失败')
        return False
    """
    {"code":2,"msg":"提交成功","smsid":"16906979568648248914"} <class 'str'>
    短信发送成功,请留意手机
    
    # 短信长这样:
        【互亿无线】您的验证码是:3476。请不要把验证码泄露给其他人。
    """

if __name__ == '__main__':
    send_sms('18211101742')

响应结果的状态码查询,https://www.ihuyi.com/api/sms.html

在这里插入图片描述

小结:

  • 如果腾讯云能通过签名认证,则首选腾讯云短信。想发谁就发谁,可以自定义短信模板和签名。
  • 其次是云通信短信。测试开发阶段,可以不认证,就可以进行测试,但模板和签名都是默认的,而且收短信的手机号必须是在平台注册的。而且它免费赠送了一些短信额度,比较划算。
  • 最后备选是互亿无限,它发短信稍微慢了点。有十条免费的短信,重置也不贵,10块钱200条。

在这里插入图片描述

Django4中的日志管理

参考:Django配置日志:https://www.cnblogs.com/Neeo/articles/17588553.html

python内置模块logging模块:https://www.cnblogs.com/Neeo/articles/10951734.html

按照文件大小进行切割日志

在你的settings.py中。

LOGS_DIRS = os.path.join(BASE_DIR, 'logs')
if not os.path.exists(LOGS_DIRS):
    os.makedirs(LOGS_DIRS)

# 日志
LOGGING = {
    'version': 1,  # 使用的日志模块的版本,目前官方提供的只有版本1,但是官方有可能会升级,为了避免升级出现的版本问题,所以这里固定为1
    'disable_existing_loggers': False,  # 是否禁用其他的已经存在的日志功能?肯定不能,有可能有些第三方模块在调用,所以禁用了以后,第三方模块无法捕获自身出现的异常了。
    'formatters': {  # 日志格式设置,verbose或者simple都是自定义的
        'verbose': {  # 详细格式,适合用于开发人员不在场的情况下的日志记录。
            # 格式定义:https://docs.python.org/3/library/logging.html#logrecord-attributes
            # levelname 日志等级
            # asctime   发生时间
            # module    文件名
            # process   进程ID
            # thread    线程ID
            # message   异常信息
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',  # 变量格式分隔符
        },
        'simple': {  # 简单格式,适合用于开发人员在场的情况下的终端输出
            'format': '{levelname} {message}',
            'style': '{',
        },
    },
    'filters': {  # 过滤器
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {  # 日志处理流程,console或者mail_admins都是自定义的。
        'console': {
            'level': 'DEBUG',  # 设置当前日志处理流程中的日志最低等级
            'filters': ['require_debug_true'],  # 当前日志处理流程的日志过滤
            'class': 'logging.StreamHandler',  # 当前日志处理流程的核心类,StreamHandler可以帮我们把日志信息输出到终端下
            'formatter': 'simple'  # 当前日志处理流程的日志格式
        },
        # 'mail_admins': {
        #     'level': 'ERROR',                  # 设置当前日志处理流程中的日志最低等级
        #     'class': 'django.utils.log.AdminEmailHandler',  # AdminEmailHandler可以帮我们把日志信息输出到管理员邮箱中。
        #     'filters': ['special']             # 当前日志处理流程的日志过滤
        # }
        'file': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            # 日志位置,日志文件名,日志保存目录logs必须手动创建
            'filename': '%s/django.log' % LOGS_DIRS,
            # 单个日志文件的最大值,这里我们设置300M
            'maxBytes': 300 * 1024 * 1024,
            # 备份日志文件的数量,设置最大日志数量为10
            'backupCount': 10,
            # 日志格式:详细格式
            'formatter': 'verbose',
            'encoding': 'utf-8',  # 输出日志编码
        },
    },
    'loggers': {  # 日志处理的命名空间
        'django': {
            'handlers': ['console', 'file'],  # 当基于django命名空间写入日志时,调用那几个日志处理流程
            'propagate': True,  # 是否在django命名空间对应的日志处理流程结束以后,冒泡通知其他的日志功能。True表示允许
        },
    }
}

遇到切割文件时,遇到权限报错的解决方案,Windows为例。

  1. 下载模块,pip install concurrent-log-handler

  2. 在原有的配置项中,修改

# 决定日志保存到哪个文件夹下?我这里将自动创建到项目根目录下的logs文件夹内
LOGS_DIRS = os.path.join(BASE_DIR, 'logs')
if not os.path.exists(LOGS_DIRS):
    os.makedirs(LOGS_DIRS)

# 按照文件大小进行切割日志
LOGGING = {
    'version': 1,  # 使用的日志模块的版本,目前官方提供的只有版本1,但是官方有可能会升级,为了避免升级出现的版本问题,所以这里固定为1
    'disable_existing_loggers': False,  # 是否禁用其他的已经存在的日志功能?肯定不能,有可能有些第三方模块在调用,所以禁用了以后,第三方模块无法捕获自身出现的异常了。
    'formatters': {  # 日志格式设置,verbose或者simple都是自定义的
        'verbose': {  # 详细格式,适合用于开发人员不在场的情况下的日志记录。
            # 格式定义:https://docs.python.org/3/library/logging.html#logrecord-attributes
            # levelname 日志等级
            # asctime   发生时间
            # module    文件名
            # process   进程ID
            # thread    线程ID
            # message   异常信息
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',  # 变量格式分隔符
        },
        'simple': {  # 简单格式,适合用于开发人员在场的情况下的终端输出
            'format': '{levelname} {message}',
            'style': '{',
        },
    },
    'filters': {  # 过滤器
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {  # 日志处理流程,console或者mail_admins都是自定义的。
        'console': {
            'level': 'DEBUG',  # 设置当前日志处理流程中的日志最低等级
            'filters': ['require_debug_true'],  # 当前日志处理流程的日志过滤
            'class': 'logging.StreamHandler',  # 当前日志处理流程的核心类,StreamHandler可以帮我们把日志信息输出到终端下
            'formatter': 'simple'  # 当前日志处理流程的日志格式
        },
        # 'mail_admins': {
        #     'level': 'ERROR',                  # 设置当前日志处理流程中的日志最低等级
        #     'class': 'django.utils.log.AdminEmailHandler',  # AdminEmailHandler可以帮我们把日志信息输出到管理员邮箱中。
        #     'filters': ['special']             # 当前日志处理流程的日志过滤
        # }
        'file': {
            'level': 'INFO',
            # 'class': 'logging.handlers.RotatingFileHandler',  # 默认的按照文件大小切割日志
            'class': 'concurrent_log_handler.ConcurrentRotatingFileHandler',  # Windows下安装并使用 concurrent-log-handler
            # 'class': 'cloghandler.ConcurrentRotatingFileHandler',  # Linux安装并使用 ConcurrentLogHandler
            'delay': True,  # 同时添加delay参数
            # 日志位置,日志文件名,日志保存目录logs必须手动创建
            'filename': '%s/django.log' % LOGS_DIRS,
            # 单个日志文件的最大值,这里我们设置300M
            # 'maxBytes': 300 * 1024 * 1024,
            'maxBytes': 0.1 * 1024 * 1024,
            # 备份日志文件的数量,设置最大日志数量为10
            'backupCount': 10,
            # 日志格式:详细格式
            'formatter': 'verbose',
            'encoding': 'utf-8',  # 输出日志编码
        },
    },
    'loggers': {  # 日志处理的命名空间
        'django': {
            'handlers': ['console', 'file'],  # 当基于django命名空间写入日志时,调用那几个日志处理流程
            'propagate': True,  # 是否在django命名空间对应的日志处理流程结束以后,冒泡通知其他的日志功能。True表示允许
        },
    }
}

在这里插入图片描述

按照时间进行切割日志

在你的settings.py中。

LOGS_DIRS = os.path.join(BASE_DIR, 'logs')
if not os.path.exists(LOGS_DIRS):
    os.makedirs(LOGS_DIRS)

# 日志
LOGGING = {
    'version': 1,  # 使用的日志模块的版本,目前官方提供的只有版本1,但是官方有可能会升级,为了避免升级出现的版本问题,所以这里固定为1
    'disable_existing_loggers': False,  # 是否禁用其他的已经存在的日志功能?肯定不能,有可能有些第三方模块在调用,所以禁用了以后,第三方模块无法捕获自身出现的异常了。
    'formatters': {  # 日志格式设置,verbose或者simple都是自定义的
        'verbose': {  # 详细格式,适合用于开发人员不在场的情况下的日志记录。
            # 格式定义:https://docs.python.org/3/library/logging.html#logrecord-attributes
            # levelname 日志等级
            # asctime   发生时间
            # module    文件名
            # process   进程ID
            # thread    线程ID
            # message   异常信息
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',  # 变量格式分隔符
        },
        'simple': {  # 简单格式,适合用于开发人员在场的情况下的终端输出
            'format': '{levelname} {message}',
            'style': '{',
        },
    },
    'filters': {  # 过滤器
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {  # 日志处理流程,console或者mail_admins都是自定义的。
        'console': {
            'level': 'DEBUG',  # 设置当前日志处理流程中的日志最低等级
            'filters': ['require_debug_true'],  # 当前日志处理流程的日志过滤
            'class': 'logging.StreamHandler',  # 当前日志处理流程的核心类,StreamHandler可以帮我们把日志信息输出到终端下
            'formatter': 'simple'  # 当前日志处理流程的日志格式
        },
        # 'mail_admins': {
        #     'level': 'ERROR',                  # 设置当前日志处理流程中的日志最低等级
        #     'class': 'django.utils.log.AdminEmailHandler',  # AdminEmailHandler可以帮我们把日志信息输出到管理员邮箱中。
        #     'filters': ['special']             # 当前日志处理流程的日志过滤
        # }
        'file': {
            'level': 'INFO',
            'class': 'logging.handlers.TimedRotatingFileHandler',
            # 日志位置,日志文件名,日志保存目录logs必须手动创建
            'filename': '%s/django.log' % LOGS_DIRS,
            # TimedRotatingFileHandler的参数
            # 目前设定每天一个日志文件
            # 'S'         |  秒
            # 'M'         |  分
            # 'H'         |  时
            # 'D'         |  天
            # 'W0'-'W6'   |  周一至周日
            # 'midnight'  |  每天的凌晨
            'when': 'S',  # 间间隔的类型,指定秒就不要在Windows上运行测试
            'interval': 5,  # 时间间隔
            'backupCount': 5,  # 能留几个日志文件;过数量就会丢弃掉老的日志文件
            'encoding': 'utf-8',  # 日志文本编码
        },
    },
    'loggers': {  # 日志处理的命名空间
        'django': {
            'handlers': ['console', 'file'],  # 当基于django命名空间写入日志时,调用那几个日志处理流程
            'propagate': True,  # 是否在django命名空间对应的日志处理流程结束以后,冒泡通知其他的日志功能。True表示允许
        },
    }
}

如果在Windows上,按照日期进行切割,报错,建议,不要在Windows上用按照时间进行切割的方式使用日志,请按照文件大小进行切割的方式用就行了。另外,默认的按照日期进行切割的配置,只在Windows上报错,在Linux上不报错。

最终,建议,无论是Windows还是Linux,如果不想报错,就都用按照文件大小进行切割就行了。

单独使用logger怎么做

你可以这样搞,在项目某个路径下,创建一个日志的配置文件,比如我将它创建在项目根目录下的utils/logger.py中,填写代码:

import logging.config
from django.conf import settings

logger = logging.getLogger('django')
logging.config.dictConfig(settings.LOGGING)  # logging配置

然后在你需要的地方,引入logger对象就行了:

import datetime
from django.shortcuts import render, HttpResponse
from utils.logger import logger
def index(request):
    now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

    logger.info('info.....')
    logger.warning('warning.....')
    logger.error('error.....')
    return HttpResponse(now)

在这里插入图片描述

Django4中的信号

参考:https://www.cnblogs.com/Neeo/articles/17589746.html

内置信号的基本写法,在你的项目同名文件夹下的__init__.py

# 导入相关信号
from django.core.signals import request_started, request_finished
# 以装饰器的形式激活信号,所以要先导入装饰器
from django.dispatch import receiver

@receiver(request_started)
def my_callback(sender, **kwargs):
    """ 回调函数 """
    print("my_callback", sender)

插入基本的orm用法

参考这个:https://www.cnblogs.com/Neeo/articles/10967645.html#orm%E7%AE%80%E4%BB%8B

代码就是,models.py:

from django.db import models


class User(models.Model):
    name = models.CharField(max_length=32, verbose_name='姓名')

    def __str__(self):
        return self.name

settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'day07',    		#你的数据库名称
        'USER': 'root',   		#你的数据库用户名
        'PASSWORD': '123', 	#你的数据库密码
        'HOST': '', 			#你的数据库主机,留空默认为localhost
        'PORT': '3306', 		#你的数据库端口
    }
}

# orm语句转为具体SQL的语句配置,你们可以自己在笔记中记录一下
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

注意,别忘了在根目录下打开terminal,执行下面两个命令:

python manage.py makemigrations
python manage.py migrate

urls.py

from django.contrib import admin
from django.urls import path
from api import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
]

views.py:

from django.shortcuts import render, HttpResponse
from api.models import User

def index(request):
    # 创建命令
    # obj = User.objects.create(name='zhangkai')
    # print(obj)
    # 更新方式1
    # obj = User.objects.filter(name='zhangkai1').first()
    # obj.name = "zhangkai2"
    # print(obj)
    # obj.save()


    # 更新方式2
    # User.objects.filter(name='zhangkai').update(name="zhangkai3")

    # 删除
    # User.objects.filter(name='zhangkai3').delete()

    # 查询
    User.objects.filter(name='zhangkai3')  # 根据条件查询 相当于select * from api_user where name='zhangaki';
    User.objects.all()  # 查所有,相当于select * from api_user;

    return HttpResponse("INDEX")

内置信号的用法示例

新增模型类对象,触发的相关信号

views.py:

from django.shortcuts import render, HttpResponse
from api.models import User



# ----------- 内置信号的用法 --------------
def index(request):
    # 什么时候模型类对象调用save方法?答案是创建对象的时候,和更新对象的时候
    obj = User.objects.create(name='zhangkai')
    print(obj)



    return HttpResponse("INDEX")

demo/__init__.py

from django.db.models.signals import pre_init, post_init, pre_save, post_save, pre_delete, post_delete
from django.dispatch import receiver

@receiver(pre_init)
def pre_init_callback(sender, **kwargs):
    """ 每当实例化一个 Django 模型时,这个信号都会在模型的 __init__() 方法的开头发出 """
    print('pre_init_callback', sender)
    print('pre_init_callback', kwargs)

@receiver(post_init)
def post_init_callback(sender, **kwargs):
    """ 和 pre_init 一样,但这个是在 __init__() 方法完成后发送的 """
    print('post_init_callback', sender)
    print('post_init_callback', kwargs)

@receiver(pre_save)
def pre_save_callback(sender, **kwargs):
    """ 这是在模型的 save() 方法开始时发送的 """
    print('pre_save_callback', sender)
    print('pre_save_callback', kwargs)
    print('pre_save_callback', kwargs['instance'].name)


@receiver(post_save)
def post_save_callback(sender, **kwargs):
    """ 就像 pre_save 一样,但在 save() 方法的最后发送 """
    print('post_save_callback', sender)
    print('post_save_callback', kwargs)
    print('post_save_callback', kwargs['instance'].name)

以及,打印效果:

pre_init_callback <class 'api.models.User'>
pre_init_callback {'signal': <django.db.models.signals.ModelSignal object at 0x00000159F5287DC0>, 'args': (), 'kwargs': {'name': 'zhangkai'}}
post_init_callback <class 'api.models.User'>
post_init_callback {'signal': <django.db.models.signals.ModelSignal object at 0x00000159F5287EE0>, 'instance': <User: zhangkai>}
pre_save_callback <class 'api.models.User'>
pre_save_callback {'signal': <django.db.models.signals.ModelSignal object at 0x00000159F5287FD0>, 'instance': <User: zhangkai>, 'raw': False, 'using': 'default', 'update_fields': None}
pre_save_callback zhangkai
post_save_callback <class 'api.models.User'>
post_save_callback {'signal': <django.db.models.signals.ModelSignal object at 0x00000159F52C4100>, 'instance': <User: zhangkai>, 'created': True, 'update_fields': None, 'raw': False, 'using': 'default'}
post_save_callback zhangkai
zhangkai

编辑模型类对象,触发的相关信号

views.py:

from django.shortcuts import render, HttpResponse
from api.models import User

# ----------- 内置信号的用法 --------------
def index(request):
    # 什么时候模型类对象调用save方法?答案是创建对象的时候,和更新对象的时候
    # 新增
    # obj = User.objects.create(name='zhangkai')
    # print(obj)

    # 编辑
    obj = User.objects.filter(name='zhangkai2').first()
    old = obj.name
    obj.name = "zhangkai888"
    new = obj.name
    # log.info(f'{old}-->{new}')
    obj.save()

    return HttpResponse("INDEX")

demo/__init__.py

from django.db.models.signals import pre_init, post_init, pre_save, post_save, pre_delete, post_delete
from django.dispatch import receiver

@receiver(pre_init)
def pre_init_callback(sender, **kwargs):
    """ 每当实例化一个 Django 模型时,这个信号都会在模型的 __init__() 方法的开头发出 """
    print('pre_init_callback', sender)
    print('pre_init_callback', kwargs)


@receiver(post_init)
def post_init_callback(sender, **kwargs):
    """ 和 pre_init 一样,但这个是在 __init__() 方法完成后发送的 """
    print('post_init_callback', sender)
    print('post_init_callback', kwargs)


@receiver(pre_save)
def pre_save_callback(sender, **kwargs):
    """ 这是在模型的 save() 方法开始时发送的 """
    print('pre_save_callback', sender)
    print('pre_save_callback', kwargs)
    print('pre_save_callback', kwargs['instance'].name)


@receiver(post_save)
def post_save_callback(sender, **kwargs):
    """ 就像 pre_save 一样,但在 save() 方法的最后发送 """
    print('post_save_callback', sender)
    print('post_save_callback', kwargs)
    print('post_save_callback', kwargs['instance'].name)

日志:

pre_init_callback <class 'api.models.User'>
pre_init_callback {'signal': <django.db.models.signals.ModelSignal object at 0x00000186F4937DC0>, 'args': (1, 'zhangkai2'), 'kwargs': {}}
post_init_callback <class 'api.models.User'>
post_init_callback {'signal': <django.db.models.signals.ModelSignal object at 0x00000186F4937EE0>, 'instance': <User: zhangkai2>}
zhangkai2
pre_save_callback <class 'api.models.User'>
pre_save_callback {'signal': <django.db.models.signals.ModelSignal object at 0x00000186F4937FD0>, 'instance': <User: zhangkai888>, 'raw': False, 'using': 'default', 'update_fields': None}
pre_save_callback zhangkai888
post_save_callback <class 'api.models.User'>
post_save_callback {'signal': <django.db.models.signals.ModelSignal object at 0x00000186F4974100>, 'instance': <User: zhangkai888>, 'created': False, 'update_fields': None, 'raw': False, 'using': 'default'}
post_save_callback zhangkai888

删除模型类对象,触发的相关信号

views.py:

from django.shortcuts import render, HttpResponse
from api.models import User



# ----------- 内置信号的用法 --------------

def index(request):
    # 什么时候模型类对象调用save方法?答案是创建对象的时候,和更新对象的时候
    # 新增
    # obj = User.objects.create(name='zhangkai')
    # print(obj)

    # 编辑
    # obj = User.objects.filter(name='zhangkai2').first()
    # old = obj.name
    # obj.name = "zhangkai888"
    # new = obj.name
    # # log.info(f'{old}-->{new}')
    # obj.save()

    # 删除
    obj_list = User.objects.filter(name='zhangkai888')
    # print(111, obj_list)  # <QuerySet [<User: zhangkai888>, <User: zhangkai888>, <User: zhangkai888>]>
    for obj in obj_list:
        obj.delete()

    return HttpResponse("INDEX")

demo/__init__.py

from django.db.models.signals import pre_init, post_init, pre_save, post_save, pre_delete, post_delete
from django.dispatch import receiver

@receiver(pre_init)
def pre_init_callback(sender, **kwargs):
    """ 每当实例化一个 Django 模型时,这个信号都会在模型的 __init__() 方法的开头发出 """
    print('pre_init_callback', sender)
    print('pre_init_callback', kwargs)


@receiver(post_init)
def post_init_callback(sender, **kwargs):
    """ 和 pre_init 一样,但这个是在 __init__() 方法完成后发送的 """
    print('post_init_callback', sender)
    print('post_init_callback', kwargs)





@receiver(pre_save)
def pre_save_callback(sender, **kwargs):
    """ 这是在模型的 save() 方法开始时发送的 """
    print('pre_save_callback', sender)
    print('pre_save_callback', kwargs)
    print('pre_save_callback', kwargs['instance'].name)


@receiver(post_save)
def post_save_callback(sender, **kwargs):
    """ 就像 pre_save 一样,但在 save() 方法的最后发送 """
    print('post_save_callback', sender)
    print('post_save_callback', kwargs)
    print('post_save_callback', kwargs['instance'].name)


@receiver(pre_delete)
def pre_delete_callback(sender, **kwargs):
    """ 在模型的 delete() 方法和查询集的 delete() 方法开始时发送 """
    print('pre_delete_callback', sender)
    print('pre_delete_callback', kwargs)


@receiver(post_delete)
def post_delete_callback(sender, **kwargs):
    """ 就像 pre_delete 一样,但在模型的 delete() 方法和查询集的 delete() 方法结束时发送 """
    print('post_delete_callback', sender)
    print('post_delete_callback', kwargs)

日志:

post_init_callback {'signal': <django.db.models.signals.ModelSignal object at 0x0000026FD6FE7EE0>, 'instance': <User: zhangkai888>}
pre_delete_callback <class 'api.models.User'>
pre_delete_callback {'signal': <django.db.models.signals.ModelSignal object at 0x0000026FD70241F0>, 'instance': <User: zhangkai888>, 'using': 'default', 'origin': <User: zhangkai888>}
post_delete_callback <class 'api.models.User'>
post_delete_callback {'signal': <django.db.models.signals.ModelSignal object at 0x0000026FD70242E0>, 'instance': <User: zhangkai888>, 'using': 'default', 'origin': <User: zhangkai888>}
pre_delete_callback <class 'api.models.User'>
pre_delete_callback {'signal': <django.db.models.signals.ModelSignal object at 0x0000026FD70241F0>, 'instance': <User: zhangkai888>, 'using': 'default', 'origin': <User: zhangkai888>}
post_delete_callback <class 'api.models.User'>
post_delete_callback {'signal': <django.db.models.signals.ModelSignal object at 0x0000026FD70242E0>, 'instance': <User: zhangkai888>, 'using': 'default', 'origin': <User: zhangkai888>}
pre_delete_callback <class 'api.models.User'>
pre_delete_callback {'signal': <django.db.models.signals.ModelSignal object at 0x0000026FD70241F0>, 'instance': <User: zhangkai888>, 'using': 'default', 'origin': <User: zhangkai888>}
post_delete_callback <class 'api.models.User'>
post_delete_callback {'signal': <django.db.models.signals.ModelSignal object at 0x0000026FD70242E0>, 'instance': <User: zhangkai888>, 'using': 'default', 'origin': <User: zhangkai888>}

Django4中的缓存

详细文档参考:https://www.cnblogs.com/Neeo/articles/17589834.html

Django支持的缓存有好几种:

  • 三方的Redis(推荐),Memcached(不推荐)
  • 缓存到本地文件
  • 缓存到本地数据库
  • 缓存到内存里
  • 虚拟缓存

在这里插入图片描述

缓存的粒度

局部视图缓存

缓存指定的视图函数,有两种写法.

  1. 在视图中以装饰器的形式

views.py

import datetime
from django.shortcuts import render, HttpResponse

# 必须导入缓存装饰器
from django.views.decorators.cache import cache_page


@cache_page(5)  # 缓存单位:秒
def index(request):
    # print(1111)
    now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    return render(request, 'index.html', {"now": now})

urls.py:

from django.contrib import admin
from django.urls import path
from api import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
]
  1. 在路由中实现

views.py

import datetime
from django.shortcuts import render, HttpResponse


def index(request):
    # print(1111)
    now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    return render(request, 'index.html', {"now": now})

urls.py

from django.contrib import admin
from django.urls import path
from api import views

from django.views.decorators.cache import cache_page
urlpatterns = [
    path('admin/', admin.site.urls),
    # path('index/', views.index),
    path('index/', cache_page(5)(views.index)),  # 缓存,路由中指定缓存的视图函数
]

模板缓存,粒度更新,相当于对于页面的局部进行缓存

views.py中正常写代码:

import datetime
from django.shortcuts import render, HttpResponse
# 必须导入缓存装饰器
# from django.views.decorators.cache import cache_page
#
#
# @cache_page(5)  # 缓存单位:秒
def index(request):
    # print(1111)
    now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    return render(request, 'index.html', {"now": now})

urls.py正常写代码:

from django.contrib import admin
from django.urls import path
from api import views

# from django.views.decorators.cache import cache_page
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    # path('index/', cache_page(5)(views.index)),  # 缓存,路由中指定缓存的视图函数
]

index.html这里就需要注意了。

  1. 必须load cache
  2. 必须用缓存的模板把要缓存的内容包起来,才能被缓存上,其它没包裹的标签,不缓存。
{% load cache %} <!-- 必须声明 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>没有缓存的</h3>
<p>{{ now }}</p>

<h3>有缓存的</h3>
<!-- cache 后面的5,表示缓存的时间,5后面的字符串来自于settings.py中的缓存配置中的LOCATION的值'unique-snowflake'-->
<!-- 用缓存的模板把要缓存的内容包起来-->
{% cache 5 'unique-snowflake' %}
    <p>{{ now }}</p>
    <p>{{ now }}</p>
{% endcache %}
</body>
</html>

全栈缓存

就是整个项目进行缓存,粒度是最大的。

首先要配置settings.py

MIDDLEWARE = [
    # 下面这个缓存中间件必须放在所有中间件的最上面
    "django.middleware.cache.UpdateCacheMiddleware",

    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',

    # 下面这个缓存中间件必须放在所有中间件的最下面
    "django.middleware.cache.FetchFromCacheMiddleware",
]



# 我这里将cache缓存由本地内存缓存更换为了Redis
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://:@127.0.0.1:6379/2",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "PASSWORD": "1234",  # 密码,如果没有设置密码,这个参数可以注视掉
            # 'MAX_ENTRIES': 300,  # 最大缓存个数(默认300)
            # 'CULL_FREQUENCY': 3,  # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
        }
    }
}


# 默认超时时间是300秒,我们可以通过CACHE_MIDDLEWARE_SECONDS来修改
CACHE_MIDDLEWARE_SECONDS = 20
# 下面是关于key的,咱们这里保持默认就完了
CACHE_MIDDLEWARE_KEY_PREFIX = ""
# 用于存储的缓存别名,没想好的,指定个default就行
CACHE_MIDDLEWARE_ALIAS = "default"

views.py正常写代码:

import datetime
from django.shortcuts import render, HttpResponse
# 必须导入缓存装饰器
# from django.views.decorators.cache import cache_page
#
#
# @cache_page(5)  # 缓存单位:秒
def index(request):
    # print(1111)
    now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    return render(request, 'index.html', {"now": now})

urls.py正常写代码:

from django.contrib import admin
from django.urls import path
from api import views

# from django.views.decorators.cache import cache_page
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    # path('index/', cache_page(5)(views.index)),  # 缓存,路由中指定缓存的视图函数
]

index.html也正常写代码:

{% load cache %} <!-- 必须声明 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>没有缓存的</h3>
<p>{{ now }}</p>

<h3>有缓存的</h3>
{% cache 5 'unique-snowflake' %}
    <p>{{ now }}</p>
    <p>{{ now }}</p>
{% endcache %}


</body>
</html>

注意,如果同时使用了全栈缓存和局部模板片段缓存,那么全栈缓存的优先级高。

Redis

数据库排行榜:https://db-engines.com/en/ranking

redis是一个独立的非关系型数据。

官方建议,将Redis安装到Linux系统,所以,你在redis官网,压根看不到redis关于Windows的安装包。

redis3 for Windows

参考:https://www.cnblogs.com/Neeo/articles/12673194.html#windows

和讲解视频

redis3 for centos

参考:https://www.cnblogs.com/Neeo/articles/12673194.html#redis307-for-centos79

和讲解视频

注意,必须关闭你的centos系统的防火墙:

# 查看防火墙状态
systemctl status firewalld.service
# 关闭防火墙
systemctl stop firewalld.service
# 禁止开机启动防火墙
systemctl disable firewalld.service
# 启动防火墙
systemctl start firewalld.service
# 防火墙随系统开启启动
systemctl enable firewalld.service

# 关闭selinux
[root@r ~]# sed -i.ori 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config

关于Redis使用和python如何操作

参考我的博客:https://www.cnblogs.com/Neeo/p/10864123.html#database

在这里插入图片描述

关于Django如何操作Redis?参考:https://www.cnblogs.com/Neeo/articles/14269422.html

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NSSCTF Round# 16 Basic pwn方向题解

pwn nc_pwnre 没有附件&#xff0c;nc直接连接 给了一段汇编代码&#xff0c;让gpt翻译一下 这段汇编代码是一个循环&#xff0c;它对存储在ebpi位置的字符串进行处理。让我逐步解释一下每个指令的作用&#xff1a;mov eax, [ebpi]: 将ebpi位置的值加载到eax寄存器中。 add e…

【汇编要笑着学】汇编模块化编程 | call和ret调用指令 | jmp跳转指令 | inc自加指令

Ⅰ.汇编模块化编程 0x00 一个简单的例子 我们了解模块化编程前先给出一个例子&#xff0c;方便大家快速了解。 SECTION MBR vstart0x7c00 ; 起始地址编译在0x7c00mov ax,cs mov ds,ax mov es,axmov ss,axmov fs,axmov sp,0x7c00 ; 上面这些都没什…

系列十、Spring Security登录接口添加验证码

一、Spring Security登录接口添加验证码 1.1、概述 一般企业开发中&#xff0c;登录时都会有一个验证码&#xff0c;基于Spring Security的登录接口默认是没有验证码的&#xff1f;那么如何把验证码功能集成到Spring Security的登录接口呢&#xff1f;请看下文&#xff01; 1.…

基于SSM的仓库在线管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

C++学习笔记——输入、输出和文件

目录 一、标准输入输出 2.1下面是它们的基本用法 解释 二、格式化输入输出 2.2下面是一个示例 解释 三、文件读写 3.3下面是一个文件读写的示例 解释 四、异常处理和错误检测 4.1下面是一个示例 解释 五、一个实例代码 5.1如何读取 CSV 文件&#xff0c;并计算每…

大厂是怎么做支付系统的流程容错的?

点击下方“JavaEdge”&#xff0c;选择“设为星标” 第一时间关注技术干货&#xff01; 1 单据关联 如某些订单域内部的多种单据间存在关联关系一样&#xff0c;支付设计上也有单据间关联设计。如所有逆向过程都须持有正向的单据&#xff0c;因此退款须关联到原来的支付&#…

LTESniffer:一款功能强大的LTE上下行链路安全监控工具

关于LTESniffer LTESniffer是一款功能强大的LTE上下行链路安全监控工具&#xff0c;该工具是一款针对LTE的安全开源工具。 该工具首先可以解码物理下行控制信道&#xff08;PDCCH&#xff09;并获取所有活动用户的下行链路控制信息&#xff08;DCI&#xff09;和无线网络临时…

为了这口醋,包的这饺子。为了Selenium,学有限的CSS,逐步替换XPATH

Learn about CSS rules and pseudo-classes to help you move your XPATH locators to CSS. 1. 最基本IdElement TypeDirect ChildChild or Sub-ChildClass 2. 深入一点Next SiblingAttribute ValuesChoosing a Specific Match Sub-String Matches 3 参考资料 In order for Sel…

Java零基础教学文档第五篇:jQuery

今日新篇章 【jQuery】 【主要内容】 jQuery简介 jQuery安装 jQuery语法 jQuery选择器 jQuery事件处理 jQueryDOM操作 jQuery元素遍历 jQuery过滤 jQuery其它方法 【学习目标】 1.jQuery简介 1.1 jQuery简介 jQuery 库可以通过一行简单的标记被添加到网页中。 1.…

java自动化将用例和截图一起执行测试放入world中直接生成测试报告【搬代码】

1.首先我们得用例写好之后放入文档中&#xff0c;把不用的案例类型、前置条件去掉之后&#xff0c;如图&#xff1a; 放到桌面后&#xff0c;先看执行结果&#xff1a; 直接上代码 package com.znzdh.qitagongju;import jxl.Sheet; import jxl.Workbook; import org.apache…

了解集群,以及集群是什么?

每个集群即一个独立运行的文档数据库&#xff0c;分片集群架构由路由&#xff08;mongos&#xff09;、配置&#xff08;config&#xff09;和分片&#xff08;shard&#xff09;组成。 数据读写请求经mongos分发&#xff0c;通过查询config信息&#xff0c;并行分配到相应sha…

C语言之字符串和指针

目录 用数组实现的字符串和用指针实现的字符串 █用数组实现的字符串str █用指针实现的字符串ptr 注意 用数组和指针实现字符串的不同点 字符串数组 用数组实现的字符串的数组——二维数组 用指针实现的字符串数组——指针数组 注意 字符串和指针有着紧密的联系&#…

力扣(105. 从前序与中序遍历序列构造二叉树,106. 从中序与后序遍历序列构造二叉树)

题目1链接 题目1&#xff1a; 思路&#xff1a;使用前序确定根&#xff0c;使用中序分左右子树&#xff0c;分治法。 难点&#xff1a;如何控制递归确定左右子树。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* T…

【STM32】FLASH闪存

1 FLASH闪存简介 本节所指STM32内部闪存&#xff0c;即下载程序的时候&#xff0c;程序存储的地方。&#xff08;非易失性&#xff09; STM32F1系列的FLASH包含程序存储器、系统存储器&#xff08;bootloader&#xff0c;不允许修改&#xff09;和选项字节三个部分&#xff0…

华西建筑智能化寻找志同道合的创业团队

我今天四十多了&#xff0c;之前也创过业&#xff0c;做软件开发系统集成的。 19年进入华西建筑装饰工程有限公司负责机电安装及弱电智能化版块。后公司成立建筑智能化事业部&#xff0c;我负责。现在想全身心打造施工企业项目管理平台&#xff0c;同时进军智慧康养领域。我想…

1.5矩阵元素的引用

通过下标来引用矩阵的元素 A(3, 2)表示A矩阵第3行第2列的元素。 >> arr [1,2,3;4,5,6]; >> arr(4, 5) 10arr 1 2 3 0 04 5 6 0 00 0 0 0 00 0 0 0 10>> 如果引用元素超过矩阵的大小将自…

Windows下面基于pgsql15的备份和恢复

一、基础备份 1.创建一个文件用来存储备份数据 2.备份指令 $CurrentDate Get-Date -Format "yyyy-MM-dd" $OutputDirectory "D:\PgsqData\pg_base\$CurrentDate" $Command "./pg_basebackup -h 127.0.0.1 -U postgres -Ft -Pv -Xf -z -Z5 -D $O…

Navicat 16 for MySQL:打造高效数据库开发管理工具

随着数据的快速增长和复杂性的提升&#xff0c;数据库成为了现代应用开发中不可或缺的一部分。而在MySQL数据库领域&#xff0c;Navicat 16 for MySQL作为一款强大的数据库开发管理工具&#xff0c;正受到越来越多开发者的青睐。 Navicat 16 for MySQL拥有丰富的功能和直观的界…

功能权限篇

文章目录 1. 如何设计一套权限系统1.1 目标1.2 权限模型1.2.1 模型一RBAC1.2.2 模型二ABAC 2.如何实现菜单的创建&#xff1f;2.1 表结构2.2 前端实现2.3 后端实现 3. 如何实现角色的创建&#xff1f;4.如何给用户分配权限 —— 将菜单赋予角色&#xff1f;5.如何给用户分配权限…

sqlilabs第五十三五十四关

Less-51(GET - GET - Error based - ORDER BY CLAUSE-String- Stacked injection) 手工注入 单引号闭合&#xff0c;和上一关一样堆叠注入解决 自动注入 和上一关一样 Less-52(GET - challenge - Union- 10 queries allowed -Variation 1) 手工注入 这一关开始后面的可以看…