【django-vue】封裝logger 封裝全域性異常 封裝response 資料庫配置 使用者表繼承AbstractUser配置

passion2021發表於2023-02-27

上節回顧

這是一個django-Vue前後端分離的專案(影片網站)

# 1 企業軟體型別
	-商城類
    -公司內部:
    	-業務
        -RBAC許可權管理
        
# 2 軟體開發流程
	-你的工作流程
    
    
# 3 pip換源 :國內映象
	-命令列中
    -pycharm中
    
    
# 4 虛擬環境:
	-每個專案有自己的環境
    	-虛擬環境:virtualenv
        -環境變數:mac:linux+win
        -梳理一下你電腦的環境:python2,python3.6,python3.10 ,anaconda
        	pip3 install  裝在哪個直譯器
            (luffy)
        -
        
    -java:Maven
    -go:go mod 模式  11.1加入
    -vue:node_models
    
    
# 5 專案目錄調整
	-1 專案啟動是載入配置檔案,再執行
    	-配置檔案路徑一定要對
        -python manage.py runserver 
    	   python xx.py lqz 19
        -配置檔案有錯,專案起不來
        
    -2 專案上線階段,使用wsgi.py 執行
    
    -3 把所有app都放在apps資料夾下,後期再配置檔案中註冊還是隻用app的名字註冊
        -pyhton包匯入的原理
        	-import  去環境變數中找 sys.path
            -為什麼匯入系統提供的包,可以直接匯入
            -為什麼裝的第三方包,可以直接匯入
            -pycharm中,專案根路徑會被加入環境變數,如果是命令列中不會
        -把apps路徑加入環境變數,以後可以基於apps這個資料夾開始導起
        -把小luffy_api 加入環境變數
    		sys.path.append(str(BASE_DIR))

python執行流程

image-20230222201311877

我們寫的python指令碼是怎麼執行的?
首先Py檔案肯定執行在作業系統之上,假設我們在作業系統上裝了python3.6的直譯器。python3.6實際上就是一個python.exe可執行檔案。

python.exe執行在作業系統之上,會生成一個程式,這叫python程式。然後py檔案就執行在這個程式裡面,並且直譯器需要一行一行的讀,你寫的python程式碼。讀完了之後,直譯器還要執行這些程式碼。所以一個py檔案執行的程式也可以說是一個python直譯器的程式。

多程式和多執行緒:
使用python多開一個程式,實際上就是在啟動一個python直譯器。
多執行緒是一個程式下可以有多個執行緒。

如果想要把我們寫的程式碼,編譯成一個可執行檔案比如xx.exe,這樣不需要直譯器也可以在作業系統上直接執行。可以使用pyintaller庫(實際上是將直譯器環境和你寫的程式碼進行打包),但是這個編譯後的檔案只能在win上使用,不能跨平臺。go可以實現跨平臺編譯。

專案目錄調整(重要)

檢視manage.py:

image-20230222204357363

專案一啟動就會去匯入配置檔案,此時如果你在配置檔案中如果匯入了專案相關的模組就會報錯。原因是,專案都還沒起來,就去匯入專案裡的東西。所以不要在setting檔案中隨便匯入模組。

image-20230222202720789

在命令列裡輸入引數:

# sys.argv
python xx.py 引數 引數...
可以透過sys.argv獲取這些引數

# 示例:
python test.py passion 123

sys.argv終端輸出:
['tests.py', 'passion', '123']

檢視execute_from_command_line函式:

image-20230222203955594

檢視execute類:

image-20230222204052952

專案上線時,還需要修改wsgi.py檔案中的配置檔案設定:

image-20230222204720651

注意:開發環境,不能使用正式上線的資料庫。開發環境--> 測試環境 --> 上線環境(微盟刪庫事件)

關於環境變數的問題

# 1 相對匯入和絕對匯入
	-相對匯入必須從環境變數下開始導 sys.path
    	-如果報包找不到的錯,確認環境變數
        
    -絕對匯入,以當前檔案為基準匯入
    	-它不能以指令碼形式執行,只能當包用
        
# 2 匯入包,pycharm提示錯誤,不一定真的有錯,只要再環境變數中,就沒有問題
	-想讓pycharm不報錯,把你加入到環境變數的路徑,設為source root

新建一個python包:

image-20230222195237929

在main.py檔案匯入s1檔案:

image-20230222195403604

在s2.py檔案匯入s1:

image-20230222195529896

如何使用相對匯入,s2匯入s1?

image-20230222195559645

使用相對匯入的檔案,該檔案就不能以指令碼形式執行了:

image-20230222195651664

會直接報錯:

image-20230222195912367

在main.py中匯入s2(可以將s2以匯入的形式執行):

image-20230222195936494

總結:在包內部,建議使用相對匯入。正常情況下,他人安裝了你的包,會自動將你的包的路徑匯入環境變數。此時使用絕對匯入不會報錯。但是如果它複製你的原始碼直接執行,這種情況使用絕對匯入會報錯,而使用相對匯入不會報錯。

給sys.path新增環境變數:(pycharm報錯,程式不一定報錯)

image-20230222210242032

設定source root:

image-20230222210310286

今日內容

1 django後端配置之封裝logger

# 專案肯定要記錄日誌
	-日誌都可以列印到控制檯
    -日誌可以寫到日誌檔案這
	-日誌存到某個庫中
    -所有專案日誌統一管理 
# sentry
使用django寫的日誌服務軟體,收集日誌,可以展示日誌,相容很多語言,是開源的。

# 以後在專案中不要出現print了,以後都用日誌logger.info(),以後專案上線,只需要調整日誌級別,低階別的日誌就不列印了,於是日誌輸出不用刪掉

# 每個專案,都需要記錄日誌
	-後期可以透過日誌排查問題,分析錯誤
    -分析使用者行為...
    
# 之前學過logging模組,django就是基於原生的logging模組。
	-django中整合日誌步驟,django使用的就是python內建的日誌模組。
	-所以django尋找配置檔案也是查詢LOGGING這個名字。

# 操作步驟:
	-第一步:在配置檔案中加入 日誌配置 ---大字典
    	-詳情見下面的配置檔案
     -第二步:在utils中新建common_logger.py ,得到日誌物件
        import logging
        # 透過配置問中的名字拿到logger物件,以後只需要匯入,直接使用物件寫日誌即可
        logger=logging.getLogger('django')
        
    -第三步:在想用的地方,匯入使用即可(注意路徑)
    from utils.common_logger import logger
        logger.info('info級別的日誌')
    	logger.error('error級別的日誌')
   

dev.py:

# 真實專案上線後,日誌檔案列印級別不能過低,因為一次日誌記錄就是一次檔案io操作
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, # 是否讓日誌資訊繼續冒泡給其他的日誌處理系統
        },
    }
}

utils/logging.py:

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

使用:

image-20230227093024964

注意:也可以在utils包的__init__.py進行註冊,以後就直接匯入util包即可。

2 後端配置之封裝全域性異常

# 前端 要接收的格式,要統一,無論後端是否出錯

# 三大認證:
檢視類的方法中只要出了異常,就會執行一個函式,但是這個函式只能處理drf的異常, 所以我們需要自己寫個函式,既能處理drf異常,又能處理django異常,這樣統一返回格式,前端看到格式都統一了

# drf異常原始碼分析 
檢視APIView的dispatch方法 ---> 三大認證中丟擲異常會被捕獲--->異常捕獲後會執行self.handle_exception(exc) ---> 省略一波原始碼的跳來跳去 ---> 發現會去執行views.py下的handle_exception函式,這個函式只會處理drf的異常 ---> 並且執行這個函式是在drf配置檔案進行了預設配置 ---> 修改這個配置將其替換成我們的異常捕獲函式

# 寫一個函式,封裝全域性異常
	-1 統一返回格式
    -2 記錄日誌:出了異常,程式有問題,後期排查問題

# 使用步驟
	-第一步:在utils中新建 common_exceptions.py
    -第二步:寫個函式
    	-看專案程式碼
    
    -第三步:配置配置檔案,以後只要出了異常,都會走我們們的函式
    	REST_FRAMEWORK = {
            'EXCEPTION_HANDLER': 'utils.common_exceptions.exception_handler',
        }
    -第四步:勇敢大膽寫程式碼,即便報錯,程式不會蹦,並且會記錄日誌,並且處理成統一格式

自定義全域性異常程式碼:


1. exceptions.py

    from rest_framework.views import exception_handler as drf_exception_handler
    from rest_framework.response import Response
    from utils.common_logger import logger

    def 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
    
2. 在配置檔案中配置

        REST_FRAMEWORK = {
        # 以後,只要出異常,就會執行exception_handler
            'EXCEPTION_HANDLER': 'utils.exceptions.exception_handler',
        }

補充說明

斷點除錯說明:

image-20220705111458073

context相關原始碼:

image-20230227101444082

request.get_full_path() 和request.path區別:

# request.get_full_path() 和request.path區別

1.兩者都是獲取request請求的url路徑

2.request.get_full_path() ==> 獲取當前url,(包含引數)

  比如傳送一個請求的路徑為127.0.0.1:8080/class_list/?name = 10
  request.get_full_path返回的是=>/class_list/?name=10
  request.path獲取的是不帶引數的路徑=>/class_list/

3.如果遇到url中含有中文的想要正常接收返回值需要對返回值就行解碼(url預設的編碼格式為unicode)

  request.get_full_path.encode('utf8')
  request.path.encode('utf8')

如果是匿名使用者,request.pk = None

image-20230227102256934

3 後端配置之二次封裝response

# drf提供的Response物件,不能很方便的加入code和msg欄位,自己封裝一個Response類,以後都用我們自己封裝的,方便我們們寫code和msg
	-{code:100,msg:提示,data:{}/[]}
    -{code:100,msg:提示,token:asdfasd,user:lqz}

# 使用步驟
	第一步:在utils下新建common_response.py
    第二步:封裝APIRespon
    第三步:匯入使用,檢視函式的方法,返回時,都使用我們們自己的

# 封裝步驟:
	1 在utils/response.py
    from rest_framework.response import Response
    class APIResponse(Response):
        def __init__(self, code=100, msg='成功', status=None, headers=None, **kwargs):
            data = {'code': code, 'msg': msg}
            if kwargs:
                data.update(kwargs)
            super().__init__(data=data, status=status, headers=headers)
            
    2 以後再檢視類中,匯入使用即可
    	return APIResponse(token='asfdasfd')  # 登入成功
    	return APIResponse(token='asfdasfd',status=201,code=101)  # 修改http響應狀態碼
    	return APIResponse(code=1001,msg='請聯絡管理員')  # 修改自己的狀態碼
    	return APIResponse(headers={"Access-Control-Allow-Origin":"*"})  # 響應頭新增東西 
    	return APIResponse(result=serializer.data)  # 獲取全部
   		return APIResponse(result=[{id:1,'name':'金梅'},{id:2,'name':'西遊記'}])

檢視drf Response原始碼:

image-20230222215704059

重寫Response類的__init__方法。

注意:我們需要保留原來的Response的功能,並且做一些擴充套件。

舉個例子:

# 希望實現如下兩個效果

# 後端返回格式
return APIResponse(token='sadasdsaddsa')
# 前端接受
{code:100, msg:'成功', token:'sadasdsaddsa'}

# 後端返回格式
return APIResponse(result=[{id:1,'name':'金梅'},{id:2,'name':'西遊記'}])
# 前端接受
{code:100, msg:'成功', token:'sadasdsaddsa', result:[{id:1,'name':'金梅'},{id:2,'name':'西遊記'}]}

如果不傳入code、msg等引數,則預設code=100msg='成功',這裡父類還有可以傳很多引數,我們這裡只用一部分。image-20230222220747342

字典的updata方法:

image-20230222221004132

基於原字典進行更新。

image-20230222221050359

如果原字典沒有就新增,如果有對應的鍵就更新。

呼叫父類的__init__:

image-20230222221355020

對於登入介面:

image-20230222222826229

對於獲取所有介面:

image-20230222222859142

檢視前端:

image-20230222222911630

4 資料庫配置


# luffy專案使用mysql5.7
安裝參考:https://www.jb51.net/article/212626.htm

# 之前使用root使用者作為專案的資料庫使用者,許可權太高了,透過root使用者登入之後,可以修改所有的資料庫。一般公司裡,給專案單獨建立一個使用者,這個使用者只對當前庫(當前專案使用的庫)有許可權。

需求:
1.建立一個名為luffy的mysql資料庫。
2.建立一個名為luffy_api的使用者,該使用者只能檢視luffy庫。


# 一、在mysql中建立一個使用者luffy_api,給使用者授予luffy庫的所有許可權
    -1 遠端連結mysql,建立一個luffy庫
    	-命令列建立
             1.管理員連線資料庫
             >: mysql -uroot -proot

             2.建立資料庫
             >: create database luffy default charset=utf8;
        -navicate客戶端建立
        
    -2 在建立之前,可以先檢視有哪些使用者
		select user,host,password from mysql.user;
    	現在公司主流使用5.7版本之後的mysql,mysql5.8叫mysql 8, msyql5.7稱之為msyql 7,可以用如下命令,檢視使用者:
        select user,host,authentication_string from mysql.user;
        
   -3 建立一個luffy_api使用者(之前有個root使用者,許可權很高)
        # 授權賬號命令模板:
        grant 許可權(create, update) on 庫.表 to '賬號'@'host' identified by '密碼'
        
        # 把luffy庫下所有表的許可權(grant all privileges)都授予luffy_api這個使用者,允許遠端連結
        grant all privileges on luffy.* to 'luffy_api'@'%' identified by 'Luffy123?';
   
'''
      注意:在mysql的不同版本上,如果只授予了使用者遠端連線的許可權,沒有授予本地連線,那麼可能會出現執行本地連線時連線不上,只能遠端連線的情況。所以可以使用以下命令
'''

        # 把luffy庫下所有表的許可權都授予luffy_api這個使用者,允許本地連結,且將本地連線密碼設定的和遠端連線的相同。(也可以設定密碼不相同)
        grant all privileges on luffy.* to 'luffy_api'@'localhost' identified by 'Luffy123?';
        
'''
	建立了本地連線,又建立遠端連線,mysql中會有兩條記錄(有兩個luffy使用者)
''' 
   -4 重新整理一下許可權
		-flush privileges;
    	說明:往資料表插入一條記錄(硬碟)---》 記憶體中沒有這個許可權 ---》 flush privileges ---> 將硬碟中的許可權讀到記憶體 
   
   -5 以luffy_api使用者登入,show databases檢視,只能看到luffy庫
      	-至此已經完成使用者建立
     
'''
只能操作luffy資料庫的賬戶
賬號:luffy_api
密碼:Luffy123?
'''

        
# 二、在專案中配置好使用mysql資料
	1 在配置中:
    import os
    user = os.environ.get('USER','luffy_api')
    password = os.environ.get('PASSWORD','Luffy123?')	
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'luffy',
            'USER': user,
            'PASSWORD': password,
            'HOST': '127.0.0.1',
            'PORT': 3306
        }
    }
    
    2 執行專案會報錯:
        django預設使用mysqlDB操作mysql,mysqlDB這個模組,在python2可以的,在python3中不支援,於是我們們使用pymysql替換,到了django2.0.7以後,如果使用pymysql替換,需要改django的原始碼,後期使用mysqlclient,替換pymysql,mysqlclient是mysqlDB的python3.x版本
     - 如果使用pymysql,需要改原始碼,需要執行
        import pymysql
		pymysql.install_as_MySQLdb() # 猴子補丁的體現,這行程式碼把django裡面所有mysqlDB的物件,都替換成pymysql
        
     - pymysql的使用演示:
    	conn
        cursor.execute('select * from user')
        
    -猴子補丁是:在
    程式執行過程中得動態替換技術(程式碼雖然沒有改,但已經不是原來的程式碼了):https://www.liuqingzheng.top/python/Python%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/24-%E5%8D%8F%E7%A8%8B%E4%B9%8Bgevent%E6%A8%A1%E5%9D%97/
    
    gevent模組:
	使用這個模組時,執行了monkey.patch_all()。
    執行這個的作用是,因為網路請求、time.sleep這些都是同步IO,程式會等到請求得到響應返回資料才會繼續執行。
    只要執行了monkey patch再使用time\socket這些模組,使用的都是gevent給你提供的time、socket模組,相當於對這些模組全域性做了一個替換。
    你好像寫的是同步程式碼,但實際上這些操作都變成非同步了。
    原本time會導致阻塞,會使CPU離開這個執行緒,現在使用猴子補丁,替換了原來的time模組,雖然程式碼還是不變,但time不會阻塞了。
	
    阻塞time-替換--> 非阻塞time
    動態語言就會具備這種功能 ---> 動態替換
    
    -以後再django中不使用pymysql了,使用mysqlclient,不需要再執行任何補丁了
    -win,linux,mac,這個mysqlclient模組不太好裝,看人品,有時候很順利,有時候上線裝不上
    
  3 只需要裝 mysqlclient,一切都解決了
    
    
# 三、目前我們們配置檔案中,直接寫死了mysql的使用者名稱和密碼
	-可能存在的風險---》如果我的原始碼洩露了---》資料庫使用者名稱密碼就洩露---》駭客可以遠端登入---》脫庫(偷庫) 
    -B站go語言原始碼洩露 營造出充會員的假象 抖音庫存帶貨
    	-B站開源的一套Go微服務框架 --->  go-kratos
       		-推薦閱讀:https://www.cnblogs.com/liuqingzheng/p/16271927.html
    -華住漢庭的酒店入住資訊(開房記錄)洩露,原始碼洩露了,導致被脫庫
    -上海隨申辦的資料洩露 ---> 使用阿里雲的es伺服器 ---> 阿里雲伺服器洩露
    
    
# 補充:
	mysql的utf8編碼和utf8mb4的區別?
    	-utf8:一個字元,佔兩個位元組(byte--->1個byte是8個位元位  10101010)
        -utf8mb4:一個字元,佔4個位元組,表情符號
        -我們們學的utf-8:可變長可以1---4位元組表示一個字元
    
    
    
# 研究一下 以下兩種登入方式的區別?第一個快
    mysql -uroot -p
    mysql -h 192.168.1.11 -P 3306 -uroot -p

mysql5.7資料庫(推薦使用偶數版本):

image-20230227150205292

資料庫會在哪裡?
注意:mysql分為客戶端和服務端。客戶端可以遠端連結服務端。

image-20230223223331312

mysql的utf8和utf8mb4的區別

# utf8:
一個字元,佔兩個位元組(byte--->1個byte是8個位元位  10101010),有些生僻字存不了
# utf8mb4:
一個字元,佔4個位元組,可以儲存表情符號(佔四個位元組)、生僻字

# utf-8編碼 和 mysql的utf8 不是一個東西:
我們們學的utf-8是可變長的,1到4位元組表示一個字元,而mysql的utf8是固定佔兩個位元組。

# 說明
增加了這個utf8mb4的編碼,mb4就是most bytes 4的意思,專門用來相容四位元組的unicode。好在utf8mb4是utf8的超集,除了將編碼改為utf8mb4外不需要做其他轉換。當然,為了節省空間,一般情況下使用utf8也就夠了。

理論上講, UTF-8 格式使用一至六個位元組,最大能編碼 31 位字元。最新的 UTF-8 規範只使用一到四個位元組,最大能編碼21位,正好能夠表示所有的 17個 Unicode 平面。

建立資料庫是選擇utf8mb4或utf8(推薦utf8mb4):

image-20230223223637044

mysql本地連線和遠端連線的區別

mysql使用命令檢視使用者:

image-20230224210022037

可以發現有兩個luffy使用者。%的意思是允許所有地址訪問。正常情況下應該只允許一個地址訪問這個資料庫。

這是因為:一個是透過本地連線,一個是遠端連線。

image-20221104113811509

這二者是有區別的:
本地連線的密碼和遠端連線的密碼是可以不一樣的。可以有兩個密碼,對應兩個luffy使用者。

mysql本地連線和遠端連線的區別?

# 本地連線 
mysql -uroot -p
# 遠端連線
mysql -h 192.168.1.11 -P 3306 -uroot -p

# 本地連線比遠端連線快,為什麼?
遠端連線需要透過網路卡出去再回來,本地連線是透過socket檔案直接去連線mysql的。
    

推薦閱讀:

 # localhost與127.0.0.1訪問區別
     localhot(local)是不經網路卡傳輸,它不受網路防火牆和網路卡相關的的限制。 127.0.0.1是透過網路卡傳輸,依賴網路卡,並受到網路防火牆和網路卡相關的限制。一般設定程式時本地服務用localhost是最好的,localhost不會解析成ip,也不會佔用網路卡、網路資源。 有時候用localhost可以,但用127.0.0.1就不可以的情況就是在於此。猜想localhost訪問時,系統帶的本機當前使用者的許可權去訪問,而用ip的時候,等於本機是透過網路再去訪問本機,可能涉及到網路使用者的許可權。

轉載自:https://blog.csdn.net/snow__wei/article/details/103277665

允許luffy_api使用者進行本地連線和遠端連線之後:

會出現兩條記錄:

image-20230224221224818

生產環境和測試環境,資料庫連線肯定不是一個地址,該怎麼處理的?

  1. 使用兩套配置檔案

  2. 在配置檔案中資料庫配置部分新增try...except程式碼,如果一個地址連線不上,再去連線另外一個。(有點low)

django-mysqlDB歷史

# 專案操作mysql,需要安裝模組
	-pymysql
    -mysqlDB
    -mysqlclient
    
    -歷史:原來py2上有個操作mysql的模組叫mysqlDB,但到py3,沒有支援py3,django預設使用這個模組去連線mysql,預設使用-mysqlDB連線,-mysqlDB不支援py3,執行報錯
    -我們使用pymysql,作為連線mysql的資料庫模組,但是需要加程式碼
    	imprort pymysql
        pymysql.install_as_mysqldb()  # 猴子補丁
        
    -django 2.2.2以後,還使用pymysql,需要改djagno原始碼
    
    -統一使用mysqlclient來作為操作mysql的底層庫
    	-基於py2的mysqldb,在py3上重新了,但是名字改成了mysqlclient
    -使用mysqlclient,只需要安裝這個模組,不需要再寫任何程式碼,直接用即可
    
    -但是:mysqlclient 這個模組,不好裝
    	-win 一般人品好,人品好,pip install mysqlclient
        -人品不好,裝不了,centos部署專案,後面會講centos上如何裝
        

# mysqlclient
pip install mysqlclient

使用pymysql,需要在專案目錄下或者app目錄下的__init__使用猴子補丁:

image-20230227115542915

猴子補丁

# 猴子補丁
	是在程式執行過程中得動態替換技術(程式碼雖然沒有改,但已經不是原來的程式碼了):https://www.liuqingzheng.top/python/Python%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/24-%E5%8D%8F%E7%A8%8B%E4%B9%8Bgevent%E6%A8%A1%E5%9D%97/
    
# gevent模組:
	使用這個模組時,執行了monkey.patch_all()。
    執行這個的作用是,因為網路請求、time.sleep這些都是同步IO,程式會等到請求得到響應返回資料才會繼續執行。
    只要執行了monkey patch再使用time\socket這些模組,使用的都是gevent給你提供的time、socket模組,相當於對這些模組全域性做了一個替換。
    你好像寫的是同步程式碼,但實際上這些操作都變成非同步了。
    原本time會導致阻塞,會使CPU離開這個執行緒,現在使用猴子補丁,替換了原來的time模組,雖然程式碼還是不變,但time不會阻塞了。
	
    阻塞time-替換--> 非阻塞time
    動態語言就會具備這種功能 ---> 動態替換

資料庫密碼保護

# 目前我們們配置檔案中,直接寫死了mysql的使用者名稱和密碼
	-可能存在的風險---》如果我的原始碼洩露了---》資料庫使用者名稱密碼就洩露---》駭客可以遠端登入---》脫庫(偷庫) 

# 使用者名稱密碼寫死在程式碼中了,保證安全
name = os.environ.get('LUFFY_NAME', 'luffy')
password = os.environ.get('LUFFY_PASSWORD', 'Luffy123?')
# 擴充:有的公司,直接有個配置中心---》服務--》只用來存放配置檔案

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'luffy',
        'USER': name,
        'PASSWORD': password,
        'HOST': '127.0.0.1',
        'PORT': 3306
    }
}

還記得我們配的環境變數嗎?

image-20230225130335541

注意:在windows下配置環境變數可能不會立即生效,如果是mas\linx 可以使用source命令使環境變數立即生效。

匯入os模組,透過os.environ可以獲取到這個環境變數:

image-20230225130555415

檢視結果,可見獲取到了環境變數的變數值:

image-20230225130623536

可以利用這個模組,將資料庫賬號和密碼,配置在電腦的環境變數中。

image-20230225130803879

資料庫配置:

image-20230225131409990

這段程式碼的意思是,如果有PASSWORD這個環境變數,就使用PASSWORD這個變數名對應的變數值,作為密碼寫在配置檔案。如果電腦裡沒有PASSWORD這個環境變數,就使用Luffy123?作為配置檔案的資料庫密碼。

在電腦環境變數裡配置使用者名稱和密碼:

image-20230225131515086

這樣即使原始碼洩露了,也無法從配置檔案中獲取到資料庫密碼。也就是真正的密碼實際存放在你伺服器的環境變數中,只有你的伺服器被入侵了,才能獲取到這個密碼。

注意:如果報錯,可能是系統環境變數中還存在PASSWORD這個環境變數,我們可以使用別的名字比如PWD

image-20230225140013180

管理配置檔案

# 配置中心
使用配置中心 --> 配置中心提供服務 --> 只用來存放配置檔案 --> 向配置中心請求配置檔案

# 發展
單配置檔案try...except --> 兩套配置檔案dev.py\prop.py --> 配置中心

# ConfigMap
ConfigMap 是一種 API 物件,用來將非機密性的資料儲存到鍵值對中。使用時, Pods 可以將其用作環境變數、命令列引數或者儲存卷中的配置檔案。
推薦閱讀:https://kubernetes.io/zh-cn/docs/concepts/configuration/configmap/

5 使用者表繼承AbstractUser配置

# 你決定使用auth表擴寫,專案一定不要先遷移!!!先建好使用者表再遷移
	已經遷移完了,再想用auth的user表
    -刪庫,刪遷移檔案所有app
    -刪django原始碼中的admin資料夾和auth資料夾的遷移檔案

# 使用步驟
    -0 使用者表使用auth表擴寫 需要Pillow模組的支援
    	- pip install Pillow
    -1 創一個使用者app:python ../../manage.py startapp user
    -2 user 的app的models.py中擴寫使用者表
    class UserInfo(AbstractUser):
        mobile = models.CharField(max_length=11, unique=True)
        # ImageField繼承了FileField,需要pillow包的支援,只用於儲存圖片
        icon = models.ImageField(upload_to='icon', default='icon/default.png')

        class Meta:
            db_table = 'luffy_user'  # 指定在msyql資料庫中的表名
            verbose_name = '使用者表'  # 在後臺管理中顯示中文
            verbose_name_plural = verbose_name # 在列印該類的物件時,顯示使用者名稱、
        def __str__(self):
            return self.username
        
    -3 配置檔案配置,註冊app,安裝Pillow模組
    	# 使用者表的配置
		AUTH_USER_MODEL='user.UserInfo'
        
    -4 兩條命令遷移
    

遷移出錯問題

問題一:在繼承AbstractUser表之前,已經執行了表遷移:

要將django/admindjango/auth目錄下的遷移檔案都刪除掉,才能解決a遷移問題:

image-20230227123029343

image-20230227123041835

# 遇到的問題,明明luffyapi/luffyapi已經加入到環境變數,程式執行沒問題,但是表遷移,就報錯,找不到模組
	-列印了看一下,確實環境變數有,但是不是個字串,是個物件
    -程式執行,是沒問題
    -遷移有問題:配置檔案中轉成字串,就解決了

問題二:luffyapi/luffyapi已經加入到環境變數,程式執行沒問題,但是表遷移,就報錯,報錯內容為"找不到模組"

確認報錯位置:

image-20230227150614462

sys.path新增環境變數時,需要新增字串:

image-20230227150635293

注意:在專案執行的時候,sys.path裡是一個物件,並不會報錯。而在命令列中執行命令時sys.path中必須是字串,所以會報這個錯。

6 開放media訪問

字尾名影響的是用哪個軟體開啟,而不會影響檔案本身。

# 步驟
	1 在配置檔案中配置
    	MEDIA_URL = '/media/'
		MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    2 新建media資料夾,icon資料夾,放一張圖片進去
    3 路由中加入:
    from django.views.static import serve
    from django.conf import settings 
    path('media/<path:path>', serve, kwargs={'document_root': settings.MEDIA_ROOT}),
    
# 以後使用djagno的配置檔案都用這個
    from django.conf import settings
    如果這樣用,會先使用我們配置檔案的配置,如果沒有相關配置,還會去查詢django預設的配置

注意這個media資料夾不是在專案的根路徑,而是在小luffy目錄下建立:

image-20230225145430052

配置如下:

image-20230225145856070

檢視path原始碼:

image-20230225150128631

檢視_path:

image-20230225150201366

引數route就是我們的路由字尾,也就是如下部分;

image-20230225150237772

View是檢視函式的記憶體地址,也就是這個serve:

image-20230225150329976

name是路由的別名(反向解析)。

kwargs就是接受的我們傳入的字典:

image-20230225150508566

然後這個字典會作為引數傳入serve:

image-20230225150704377

會傳到serve的第三個形參。

再梳理一下,把serve當成一個FBV。
請求來了之後request會傳入serve的第一個引數。
然後我們使用了路由轉換器media/<path:path>(示例:media/www.baidu.com/,path匹配到www.baidu.com),所以第二個引數會傳入路由轉換器匹配到的東西。
第三個形參document_root,需要以一個字典的形式,透過kwargs傳入,這個引數用於設定media資料夾的位置。

7 路飛前臺專案建立和配置

# 1 建立專案
# 2 刪除一些不用的
	-App.vue中只保留
    <template>
      <div id="app">
        <router-view/>  <!-- 可以是單標籤也可以是閉合標籤 -->
      </div>
    </template>
    
# 3 HomeView.vue
    <template>
      <div class="home">
        <h1>首頁</h1>
      </div>
    </template>
    <script>
    export default {
      name: 'HomeView',
    }
    </script>
# 4 router/index.js
	const routes = [
        {
            path: '/',
            name: 'home',
            component: HomeView
        },
    ]

7.1 安裝axios

# 1 安裝
cnpm install axios

# 2 配置 main.js中
import axios from 'axios'
Vue.prototype.$axios=axios

# 3 以後再任意元件中使用
this.$axios.get(...)

配置:

image-20230225155138410

還可以在外掛、混入這兩個地方進行配置。

7.2 elementui

# vue2 使用elementui
	-安裝:cnpm i element-ui -S

    -配置:main.js中
    	import ElementUI from 'element-ui';
        import 'element-ui/lib/theme-chalk/index.css';
        Vue.use(ElementUI);
    -使用:在任意元件中複製貼上(template,script,style)

#vue3 使用 element-plus

7.3 bootstrap,jquery

有需求可以使用。

# 我們的專案沒使用,但是引入,以後可以用

# bootstrap有些事件基於jquery  ---> 使用bootstrap也需要引入jquery

# 使用步驟:
	1 安裝
    	cnpm install jquery -S
		cnpm install bootstrap@3 -S
        
    2 配置:main.js
        import 'bootstrap'
        import 'bootstrap/dist/css/bootstrap.min.css'
        
    3 vue.config.js配置
    
        const webpack = require("webpack");
        module.exports = {
            configureWebpack: {
                plugins: [
                    new webpack.ProvidePlugin({
                        $: "jquery",
                        jQuery: "jquery",
                        "window.jQuery": "jquery",
                        "window.$": "jquery",
                        Popper: ["popper.js", "default"]
                    })
                ]
            }
        };

配置vue.config.js:

方法一:直接整個覆蓋這個檔案就行。

方法二:在保留原來程式碼的基礎上,新增上面的配置。

7.4 vue-cookies

# 1 安裝
	cnpm install vue-cookies -S
# 2 配置:main.js中
	import cookies from 'vue-cookies'
    Vue.prototype.$cookies=cookies
# 3 使用:任意元件中
	this.$cookies.set()
	

8 django配置檔案說明

# pathlib 
這個模組是python3.6以後,處理檔案路徑的模組,原來是使用os模組。

# 面試題,md5是對稱加密還是非對稱加密
	-對稱加密:加密的秘鑰和解密的秘鑰是同一個
    -非對稱加密:加密使用公鑰加密,解密使用私鑰解密,使用公鑰是不能解密的
    -摘要演算法:沒有解密這一說 
  
解答:md5摘要演算法 無法解密 ---> 不存在對稱加密還是非對稱加密

配置檔案相關說明:

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/'


# 
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

pathlib說明 :

image-20230227151303780

DEBUG說明:開發時開啟,上線時關閉。

上線時配置:

image-20230227152224892

前端顯示:

image-20230227152520140

DEBUG=True時,程式碼可以熱更新,也就是可以自動重啟服務。對開發更友好,可以列出所有路徑,報了錯,前端能看到。星號表示允許部署在所有地址上。

django 自帶的APP:

  • auth app會給你建立6張表。
  • admin app註釋會報錯。
  • auth app和 admin app有關聯。

django 自帶中介軟體:

註釋django 所以自帶的app之後會導致:
drf 用不了,django-jwt用不了。所以要配置drf不使用django的認證類,並且重寫jwt。

ROOT_URLCONF根路由:

根路由移動pycharm會幫你自動修改

模板檔案:

image-20230227154943357

箭頭這裡可以選擇配置兩個類,這兩個類對應 jinja2 或者 django模板語法。
下面的模板路徑用列表,意思是可以有多個模板語法類。

專案執行的配置:
上線時使用uswgi。

資料庫配置:可以配置多個資料庫。資料庫主從。

國際化:

中國使用 --> 東八區時間

image-20230227155535310

不僅僅可以寫Shanghai寫別的也行,只要滿足東八區。

DEFAULT_AUTO_FIELD:
整型欄位 ---> 大整型欄位
這裡有一段歷史,django之前表遷移的時候會自動生成主鍵,也就是id欄位,這個欄位使用的是整型。後來django將其替換成立大整整型,防止資料量過大的情況出現問題。

擴充套件

# windows安裝 mysql 5.7
https://zhuanlan.zhihu.com/p/571585588

seo: 搜尋引擎最佳化 
Search Engine Optimization, 搜尋引擎最佳化。
利用搜尋引擎的規則提高網站在有關搜尋引擎內的自然排名。
sem:交錢,買關鍵詞,排到前面

# 比較好的部落格
	-cnblogs
    -思否
    -掘金
    -脈脈  

雜談:
多個軟體可以監聽同一個埠,但是一個埠只能給一個軟體使用。

前後端分離rbac後臺管理

# 推薦學習以下專案:
# 後臺:https://gitee.com/liuqingzheng/rbac_manager
# 前端:https://gitee.com/liuqingzheng/vue_admin

# rbac後臺管理專案相關
	-前端 ---》看人家開源的前端專案,看不懂
    -第一種情況,vue+java====》分析有哪些介面,drf複寫出來
	-第二種情況: 你新建前端專案,你看到它哪個頁面好看,copy你裡面去只要template和style  
    	-js東西自己寫

python位運算

&:按位與:兩位都為1,結果為1,否則為0
|:按位或:只要有一位為1,結果就為1
^:按位異或:兩對應的二進位相異時,結果為1
~: 按位取反,即把1變為0,把0變為1,相當於(-x-1)
<<:左移動運算子:運算數的各二進位全部左移若干位,由 << 右邊的數字指定了移動的位數,高位丟棄,低位補0。
>>:右移動運算子:把">>"左邊的運算數的各二進位全部右移若干位,>> 右邊的數字指定了移動的位數


a^b        按位異或:兩對應的二進位相異時,結果為1 
a          0011 1100
b          0000 1101
二進位制結果   0011 0001
十進位制結果   49

# 推薦閱讀: 
python位運算 https://zhuanlan.zhihu.com/p/370167569
goland位運算 https://juejin.cn/post/7090884238588772383 go語言位運算:沒有取反

練習


1 封裝日誌並測試 ok
2 封裝異常處理記錄日誌並測試 ok 
3 封裝Resposne並測試 ok
4 建立路飛使用者,路飛庫 ok 
5 建立使用者表,遷移成功 ok

---------擴充套件------
6 搜尋:utf8和utf8mb4的區別 ok 
7 搜尋:mysql本地連線和用ip連線的區別 ok
8 學習python位運算 ok
9 重灌mysql 5.7 ok

相關文章