后台日志封装

以后 项目一定需要记录日志

  • 一般日志都可以打印到控制台
  • 日志可以写道到日志文件中
  • 日志存到某个库
  • 所有项目日志统一管理(sentry:django写的服务,收集日志的,可以展示)-SDK
  • 以后再项目中,不要出现print了,以后都使用日志 logger.info(),以后上线只需要调整日志级别,低级别日志不需要打印,日志的输出不需要删掉

django中集成日志步骤:【django使用的就是python内置的日志模块】

  • 1.先在dev.py文件中配置加入 日志配置->大字典
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
        },
    },
    'filters': {
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console': {
            # 实际开发建议使用WARNING
            'level': 'DEBUG',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {
            # 实际开发建议使用ERROR
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            # 日志位置,日志文件名,日志保存目录必须手动创建,注:这里的文件路径要注意BASE_DIR代表的是小luffyapi
            'filename': os.path.join(os.path.dirname(BASE_DIR), "logs", "luffy.log"),
            # 日志文件的最大值,这里我们设置300M
            'maxBytes': 300 * 1024 * 1024,
            # 日志文件的数量,设置最大日志数量为10
            'backupCount': 10,
            # 日志格式:详细格式
            'formatter': 'verbose',
            # 文件内容编码
            'encoding': 'utf-8'
        },
    },
    # 日志对象
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'propagate': True,  # 是否让日志信息继续冒泡给其他的日志处理系统
        },
    }
}
  • 2.、在utils 新建common_logger.py得到日志对象

可以在__init__中 注册一下

from .common_log import logger

在别处即可用utils.logger.info('xx')

,直接通过 import utils 来导入 utils.logger -->logger对象

import logging
logger = logging.getLogger("django")

image.png

在需要使用的地方导入日志对象,使用即可

  • 3.在任意想用日志的地方,导入使用即可

gitee 前后端分离的rbac项目演示

前端: https://gitee.com/liuqingzheng/vue_admin
后端: https://gitee.com/liuqingzheng/rbac_manager

全局异常处理封装

前端 需要接收的格式,要统一,无论后端是否出错

三大认证:
视图类方法:
只要处理异常,就会执行一个函数,但是这个函数只能处理drf的异常
我们自己写个函数,技能处理drf异常又能处理django异常,这样统一返回格式
前端看到格式也就统一了
  • 第一步:在utils中新建 common_exceptions.py
  • 第二部:写个函数
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
from utils.common_log import logger


def common_exception_handler(exc, context):
    # 程序出了异常,会走到这,我们都要记录日志
    # 请求地址,请求方式,请求时间,请求哪个视图函数,如果登录了,记录一下用户id
    request = context.get('request')
    try:
        user_id = request.user.pk
        if not user_id:
            user_id = '匿名用户'
    except:
        user_id = '匿名用户'
    view = context.get('view')
    logger.error('用户:【%s】,使用:【%s】 请求,请求:【%s】 地址,视图函数是:【%s】,出错了,错误是:【%s】' % (
        user_id, request.method, request.get_full_path(), str(view), str(exc)
    ))
    # 第一步:执行一下原来的异常处理:它只处理drf的异常,django的异常没有处理
    # res如果有值是Response的对象,说明是drf的异常
    # res如果是None,说明是django的异常
    res = drf_exception_handler(exc, context)
    # 在这里,可以通过状态码,把异常分的更细一些:比如有数据的异常,除以0的异常,列表越界异常。。。。
    if res:
        # drf异常
        # res=Response(data={'code':999,'msg':'服务器出错,请联系系统管理员'})
        res = Response(data={'code': 999, 'msg': res.data.get('detail', '服务器出错,请联系系统管理员')})
    else:
        # django的异常,状态码是888,错误信息是  exc异常对象转成字符串
        res = Response(data={'code': 888, 'msg': str(exc)})

    return res

image.png

  • 第三步:以后只要处理异常都会走我们自定义的函数

配置文件

# drf dev.py中配置:
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'utils.exception.common_exception_handler',
}

request请求 拿 ip地址: request.META.get('REMOTE_ADDR')

  • 第四步:勇敢编写代码,即使报错,程序也不会崩,会自动记录日志,并且统一返回给前端格式,如果在出错就是这个处理异常的函数出了错

封装Response

我们想要返回前端的响应有统一的格式,我们就可以封装drf中的Response
image.png

本身 Drf自带Response,
但是公司规定,前端收到的格式都是固定的:
{'code':100,"msg":'提示',}
{'code':100,"msg":'提示',"token":'123zsxaxa'}
对Response进行封装,封装后,code,msg可以不传
  • 第一步:在utils下新建common_response.py
  • 第二步:继承Drf的Response并重写
from rest_framework.response import Response


class APIResponse(Response):
    def __init__(self, msg='ok', code=100, status=None, headers=None, **kwargs):
    
        data = {
            "code": code,
            "msg": msg
        }
        ## **kwargs接收的是自定义其他的关键字参数 比如: "username":"123"
        if kwargs:
            data.update(kwargs)
        super().__init__(status=status, headers=headers,data=data)

luffy数据库创建

创建luffy数据库
之前项目操作数据库,都是root用户,
root用户的权限很高,在公司一般不会有root用户权限
如果开发人员是root权限,数据安全性就很差
开发应该专门创建一个用户
用户只对当前有项目有操作权限
面试题: utf8 utf8mb4区别

增加了这个utf8mb4的编码,mb4就是most bytes 4的意思
专门用来兼容四字节的unicode。
好在utf8mb4是utf8的超集,除了将编码改为utf8mb4外不需要做其他转换。
当然,为了节省空间,一般情况下使用utf8也就够了。

理论上讲, UTF-8 格式使用一至六个字节,最大能编码 31 位字符。
最新的 UTF-8 规范只使用一到四个字节,最大能编码21位,正好能够表示所有的 17个 Unicode 平面。

创建数据库

"""
1.管理员连接数据库
>: mysql -uroot -proot

2.创建数据库
>: create database luffy default charset=utf8;

3.查看用户
>: select user,host,password from mysql.user;

# 5.7往后的版本
>: select user,host,authentication_string from mysql.user;
"""

为指定数据库配置指定账户

面试题:localhost 和 ip 连接 数据库有什么区别

https://blog.csdn.net/m0_58987515/article/details/125627981

"""
设置权限账号密码
# 授权账号命令:grant 权限(create, update) on 库.表 to '账号'@'host' identified by '密码'

1.配置任意ip都可以连入数据库的账户
>: grant all privileges on luffy.* to 'luffy'@'%' identified by 'Luffy123?';

2.由于数据库版本的问题,可能本地还连接不上,就给本地用户单独配置
>: grant all privileges on luffy.* to 'luffy'@'localhost' identified by 'Luffy123?';

3.刷新一下权限
>: flush privileges;

只能操作luffy数据库的账户
账号:luffy
密码:Luffy123?
"""

使用项目连接

项目需要操作mysql,需要安装模块 pymsql
在django中叫mysqlDB
mysqlclient
	历史:
原来py2上有个操作mysql的模块叫myslqDB,但是到Python3,没有支持py3,django默认使用这个模块连接mysql
默认使用-mysqlDB连接, -mysqlDB不支持py3,运行会报错


我们使用pymysql,作为连接mysql数据库的模块,就需要加代码

    import pymsql
    pymsql.install_as_myslqdb() # 猴子补丁

Django2.2.2以后还使用pymysql,需要改django源代码
所以:
    '''
    统一使用Mysqlclient来作为操作mysql的底层库
    '''
    	基于py2的myslqdb,在py3上重写了,但是名字改成了mysqlclient
    使用mysqlclient,只需要安装这个模块,不需要再编写任何代码,直接使用即可
    
    但是mysqlclient,这个模块不好装
    -windows 一般没什么问题
    -mac,linux 不太好安装
项目数据库dev.py配置
user = os.environ.get('USER', 'luffy_api')
pwd = os.environ.get('PWD', '123')
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': "luffy",
        'USER': user,
        'PASSWORD': pwd,
        'HOST': '127.0.0.1',
        'PORT': 3306
    }
}
数据库用户名/密码在django中加密

我们在数据库中编写的密码容易泄露,我们可以采取在环境变量中添加键值对的方式去加密我们的真实数据
Python实战项目2-后端封装配置处理-小白菜博客
这样我们就可以通过os.environ.get()来获取到我们所需要的

user = os.environ.get('USER', 'luffy_api') # 如果get获取不到,就采用后面的值
pwd = os.environ.get('PWD', '123')

软件开发模式

概述开发模式

无论是瀑布式开发、敏捷开发还是DevOps,整个流程都分为设计、开发、测试和部署四个部分,只不过各个部分的开始和结束时间节点不同而已!下图很好地解释了这一点。
image.png

从瀑布式开发到敏捷开发再到DevOps,各个阶段的切换速度越来越快,瀑布式开发和敏捷开发的运维部署工作都是放到最后,而 DevOps 结合敏捷开发思想,将部署工作也敏捷起来。

二、瀑布式开发

简述瀑布式开发
瀑布式开发是早期被广泛采用的软件开发模型,要求有明确的需求,按照需求一步步做好规划,每一阶段工作的完成是下一阶段工作开始的前提,每一阶段都要进行严格的评审,保证各阶段的工作做得足够好时才允许进入下一阶段,它适用于需求明确的项目。
最大的风险是,当产品研发完成后, 到了产品测试阶段如果发现了问题 ,或者发现其无法满足市场需求, 那么就需要重新开发,甚至需要重新规划产品。

三、敏捷式开发

简述敏捷式开发
敏捷开发是一种以用户需求进化为核心、迭代、循序渐进的开发方法。首先把用户最关注的软件原型做出来并交付给用户,用户在实际场景中发现问题并给予反馈,研发人员快速修改弥补需求中的不足。上述过程不断迭代,直到用户满意。
敏捷适用于需求不明确、创新性或者需要抢占市场的项目,特别适合互联网项目。

四、DevOps

DevOps是一种软件开发实践,它将人员、流程和技术结合在一起,以交付持续的价值。该方法分为计划和跟踪、开发、生成和测试、交付以及监视和操作。
DevOps 的独特之处在于开发、IT 运营、质量工程和安全团队协同工作,在发布新产品、版本或更新所涉及的所有任务中创造效率。
DevOps将开发和运营合并为一个团队,专注于快速交付和稳定的基础架构。其目标包括:

  • 将完成的代码快速交付到生产环境,
  • 最小的生产故障,
  • 从故障中立即恢复。

User模块用户表

当我们决定使用auth表扩写,项目一定不要先进行迁移,先建好用户表再进行迁移
如果迁移完成,再想使用auth的user表:
->需要进行删库,并且删除迁移文件所有app
->删除admin和auth的迁移文件
pip install Pillow ImageField字段需要pillow模块的支持 我们需要先进性安装

from django.contrib.auth.models import AbstractUser
# 用户表使用auth表扩写  
class User(AbstractUser):
    # 扩写手机号和头像字段
    mobile = models.CharField(max_length=11, unique=True)
    # 需要pillow包的支持
    icon = models.ImageField(upload_to='icon', default='icon/default.png')
    
    class Meta:
        db_table = 'luffy_user' # 默认表名
        verbose_name = '用户表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.username

Django配置文件详解

from pathlib import Path
import os
import sys

# 项目根路径
# 我们就是要让小路飞路径作为项目根路径
BASE_DIR = Path(__file__).resolve().parent.parent  # 项目根路径, 小路飞luffy_api路径 D:\pythonProject03\luffy_api\luffy_api
# print(BASE_DIR)
# 把 apps 路径加入到环境变量
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
# 把BASE_DIR也加入到环境变量,以后直接从小路飞开始导起即可
sys.path.insert(0, str(BASE_DIR))
# print(sys.path)
# 以后从大路飞开始导起,或者小路飞开始导起,或者apps开始导起都可以


# 秘钥,涉及到加密的django中,都会用它
SECRET_KEY = 'django-insecure-!g(8l%fw_#t$pz$x4jdf#e3$b4+c%xzqyq@3zki08vj&i)z4k-'

# 项目是以debug模式运行,还是非debug模式运行

# 项目上线,要改成false
# debug=True 代码可以热更新
# 调试模式下,对开发者更友好:可以列出所有路径.报了错,前端能看到
DEBUG = False
# 它搭配debug=False,它的意思是,允许我的项目部署在哪个ip地址上,* 表示允许部署在所有地址上
ALLOWED_HOSTS = ['*']



# django 是多个app组成的,里面配置app,默认带的app,django内置的app
# django 是一个大而全的框架,有很多内置app:
#   admin后台管理,
#   auth权限管理,
#   contenttypes表中存app也表的关系,
#   sessions session表,django的session相关
#   messages:消息框架,flask讲闪现,是一样的东西
#  staticfiles:静态资源的

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',

    # 'luffy_api.apps.home',   # luffy_api在环境变量,直接从这一层开始导起, 太长了,以后就想 注册  home
    # 'luffy_api.apps.user'
    'home',
    'user'
]


# 中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',  # 安全相关中间件
    'django.contrib.sessions.middleware.SessionMiddleware', # session相关中间件
    'django.middleware.common.CommonMiddleware',            # 带不带 / 问题
    'django.middleware.csrf.CsrfViewMiddleware',            # csrf 认证,生成csrf串
    'django.contrib.auth.middleware.AuthenticationMiddleware', # 用户认证
    'django.contrib.messages.middleware.MessageMiddleware',  #消息框架相关
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]


# 根路由
ROOT_URLCONF = 'luffy_api.urls'

# 模板文件
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],  # 坑,模板路径用列表,可以有多个
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]


# 项目运行的配置---》项目上线运行,使用uwsgi 运行  application()
WSGI_APPLICATION = 'luffy_api.wsgi.application'

# 用户名密码写死在代码中了,保证安全
name = os.environ.get('LUFFY_NAME', 'luffy')
password = os.environ.get('LUFFY_PASSWORD', 'Luffy123?')
# 拓展:有的公司,直接有个配置中心---》服务--》只用来存放配置文件


# 数据库配置,mysql 主从搭建完,读写分离
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'luffy',
        'USER': name,
        'PASSWORD': password,
        'HOST': '127.0.0.1',
        'PORT': 3306
    },
}

#忽略掉
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# 国际化
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False


# 静态资源
STATIC_URL = '/static/'


# 新版本django的字段。以前的自增字段是IntergeFiled,存储量怕不够,所以新增字段
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

开启media访问

配置文件中配置

配置文件中给添加:
	MEDIA_URL = '/media/'
	MEDIA_ROOT = os.path.join(BASE_DIR,'meida')

总路由中设置

re_path('^media/(?P<path>.*)$',serve,{'document_root':settings.MEDIA_ROOT}),

image.png

我们通过访问media目录下的资源就可以访问到了

练习

路飞封装日志并测试
封装异常处理记录日志并测试
封装Response并测试
创建路飞用户,路飞库

-----扩展------
搜索utf8和utf8mb4的区别
mysql本地连接和ip连接的区别
学习python位运算