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,中文路径

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

2024--Django平台开发-Django知识点(七)_第1张图片

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

2024--Django平台开发-Django知识点(七)_第2张图片

在不包含中文路径下,创建并使用虚拟环境,没问题。
2024--Django平台开发-Django知识点(七)_第3张图片

建议:

激活虚拟环境之后,下载模块之前,都要去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文件中,将项目依赖下载到当前虚拟环境中。

2024--Django平台开发-Django知识点(七)_第4张图片

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

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

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

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

额外的卸载重装python解释器

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

2024--Django平台开发-Django知识点(七)_第5张图片

关于pycharm无法创建Django项目

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

2024--Django平台开发-Django知识点(七)_第6张图片

但是,创建时,遇到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

2024--Django平台开发-Django知识点(七)_第7张图片

  1. pycharm从本地打开项目,然后添加当前虚拟环境到pycharm的解释器配置中了。
    2024--Django平台开发-Django知识点(七)_第8张图片

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

2024--Django平台开发-Django知识点(七)_第9张图片

2024--Django平台开发-Django知识点(七)_第10张图片

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

2024--Django平台开发-Django知识点(七)_第11张图片

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

2024--Django平台开发-Django知识点(七)_第12张图片

2024--Django平台开发-Django知识点(七)_第13张图片

关于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,遇到登录,就微信扫码登录。

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

2024--Django平台开发-Django知识点(七)_第14张图片
2024--Django平台开发-Django知识点(七)_第15张图片

  1. 准备好秘钥,用于获取SecretId和SecretKey,秘钥管理:https://console.cloud.tencent.com/cam/capi
    2024--Django平台开发-Django知识点(七)_第16张图片

2024--Django平台开发-Django知识点(七)_第17张图片

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

2024--Django平台开发-Django知识点(七)_第18张图片

2024--Django平台开发-Django知识点(七)_第19张图片

2024--Django平台开发-Django知识点(七)_第20张图片

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

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

    2024--Django平台开发-Django知识点(七)_第21张图片

2024--Django平台开发-Django知识点(七)_第22张图片

  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':"短信发送失败"})
    # # 接着往下写短信发送成功的代码

2024--Django平台开发-Django知识点(七)_第23张图片

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

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

参考: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

2024--Django平台开发-Django知识点(七)_第24张图片

登录

2024--Django平台开发-Django知识点(七)_第25张图片

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

2024--Django平台开发-Django知识点(七)_第26张图片

进入控制台首页:https://console.yuntongxun.com/member/main
2024--Django平台开发-Django知识点(七)_第27张图片

应用

用默认的就就行了。

2024--Django平台开发-Django知识点(七)_第28张图片

模板部分

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

手机号

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

2024--Django平台开发-Django知识点(七)_第29张图片

具体使用

2024--Django平台开发-Django知识点(七)_第30张图片

2024--Django平台开发-Django知识点(七)_第31张图片

常见报错

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

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

2024--Django平台开发-Django知识点(七)_第32张图片

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

2024--Django平台开发-Django知识点(七)_第33张图片

# -*- 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'}} 
    """
    # 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

2024--Django平台开发-Django知识点(七)_第34张图片

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

2024--Django平台开发-Django知识点(七)_第35张图片

2024--Django平台开发-Django知识点(七)_第36张图片

2024--Django平台开发-Django知识点(七)_第37张图片

2024--Django平台开发-Django知识点(七)_第38张图片

短信demo

首先,准备这两个值

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

2024--Django平台开发-Django知识点(七)_第39张图片

代码:

# -*- 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"} 
    短信发送成功,请留意手机
    
    # 短信长这样:
        【互亿无线】您的验证码是:3476。请不要把验证码泄露给其他人。
    """

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

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

2024--Django平台开发-Django知识点(七)_第40张图片

小结:

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

2024--Django平台开发-Django知识点(七)_第41张图片

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表示允许
        },
    }
}

2024--Django平台开发-Django知识点(七)_第42张图片

按照时间进行切割日志

在你的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)

2024--Django平台开发-Django知识点(七)_第43张图片

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)  # , , ]>
    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(不推荐)
  • 缓存到本地文件
  • 缓存到本地数据库
  • 缓存到内存里
  • 虚拟缓存

2024--Django平台开发-Django知识点(七)_第44张图片

缓存的粒度

局部视图缓存

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

  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>Titletitle>
head>
<body>
<h3>没有缓存的h3>
<p>{{ now }}p>

<h3>有缓存的h3>


{% 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>Titletitle>
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

2024--Django平台开发-Django知识点(七)_第45张图片

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

你可能感兴趣的:(django,python,后端)