基於django 中的settings實現原理,實現自己項目配置文件的可插拔式設計
##首先說一下django中settings.py中的實現原理
'''
應該明確一點,django暴露給用戶一個自定義配置的文件,即settings.py,用戶配置了就是優先使用用戶配置的,否則就使用默認的(from django.conf import global_settings) 同時配置文件中的變量名必須是大寫的才能生效(顯然,內部實現原理一定有判斷是否大寫)
'''
'''
其次是它的原理,通過from django.conf import settings 進入源碼發現,是通過實例化生成一個LazySettings對象(這里面用到了單例模式,下面會詳細介紹),再進源碼,看到過程如下:
1.先通過環境變量名,拿到manage.py文件中一啟動就寫進大字典(相當於一個環境變量的全集合)里的settings文件名(字符串形式的)
2.先判斷環境變量是否為空,空的話就拋異常,否則進入settings類中執行
3.執行過程就是用到dir,先循環global_settings中的各種屬性方法,然后把其中大寫的變量名(字符串)篩選出來,再通過反射的方式先從global_settings中取出值,再setattr到當前對象中
4.然后又通過importlib導入暴露給用戶的settings文件,得到對象,同樣的道理,先循環取出文件里的屬性和方法,再判斷是大寫就反射取值
5.多了一步就是是否按格式寫了(必須是元組或者列表)
6.然后反射設置值,這樣,有的話就覆蓋,沒有就用默認的。
'''
##然后講一下用到到幾個技術點:
'''
1.單例模式:settings本質就是實例化產生一個對象,但是每次運行都要開辟內存空間存放環境配置太耗資源了,就想到通過實例化一次settings對象,下次用的時候直接導入模塊就好了。
2.dir:目的是把對象中的屬性和方法全部取出來,列表的形式展示。擴展:dir() 函數不帶參數時,返回當前范圍內的變量、方法和定義的類型列表;帶參數時,返回參數的屬性、方法列表。如果參數包含方法__dir__(),該方法將被調用。如果參數不包含__dir__(),該方法將最大限度地收集參數信息。
3.反射:主要的語句就是setattr(self, a, getattr(b, c)), 意思就是,先通過c字符串在b對象中取出對應的值,然后把該值賦值給當前對象的a方法或者屬性,即self.a = getattr(b,c). 反射主要用在通過字符串判斷、取值、設置及刪除對象中的方法或者屬性。
4.importlib.import_module('settings') == import settings 前者是動態導入,可以根據前一句得到的字符串進行導入,常用場景是希望從配置文件等地獲取要被動態加載的module,但配置項通常為字符串類型,無法用import加載。
具體見頁尾連接
'''
## 最后思路講完了,話不多說,直接上代碼
'''
還是要先說兩句,先設置全局變量,給個啟動文件,然后再暴露一個配置文件讓用戶寫。
'''
#conf
#里面有暴露給用戶的settings.py
NAME = "我是暴露給用戶的自定義配置"
#lib(全局變量就在這里)
## conf(是一個包)
### __init__.py
'''
導入一個模塊時,會執行這個模塊的代碼,申請內存空間,把變量放到內存空間中,導入這個包,會執行__init__中的代碼
'''
import os
import importlib
from lib.conf import global_settings
class Settings(object):
def __init__(self):
for setting in dir(global_settings):
if setting.isupper():
k = setting
v = getattr(global_settings,setting)
setattr(self,k,v)
# 從全局的大字典中獲取到暴露給用戶的配置文件字符串路徑
mod = os.environ.get('xxx')
# mod = 'conf.settings'
module = importlib.import_module(mod)
"""
from conf import settings
module 》》》 settings
"""
for setting in dir(module):
if setting.isupper():
k = setting
v = getattr(module,setting)
setattr(self,k,v)
# 循環獲取默認的配置文件中所有的大寫配置
# 利用setattr給對象不停的設置鍵值對
# 再循環獲取暴露給用戶的自定義配置文件中所有的大寫的配置
# 再利用setattr給對象不停的設置鍵值對
settings = Settings()
### global_settings.py
NAME = '我是系統默認的全局配置'
#start.py
import os
import sys
BASE_DIR = os.path.dirname(__file__)
sys.path.append(BASE_DIR)
os.environ.setdefault('xxx','conf.settings')
from lib.conf import settings
print(settings.NAME)
## 運行后會打印 "我是暴露給用戶的自定義配置"
importlib動態導入模塊