官术网_书友最值得收藏!

3.2.2 ORM框架的源碼解析

在了解了mysqlclient模塊的常用方法后,就可以開始全面學(xué)習(xí)ORM框架了,先來看django/db/__init__.py文件中的源碼內(nèi)容。在2.4節(jié)介紹makemigrations命令時(shí)曾出現(xiàn)導(dǎo)入connections變量的語句,當(dāng)時(shí)并未詳細(xì)說明,只知道它可以用來保存各種數(shù)據(jù)庫的連接信息,下面詳細(xì)分析這個(gè)變量:

從上面的語句可知,connections變量是一個(gè)ConnectionHandler對象。繼續(xù)追蹤C(jī)onnectionHandler對象的源碼,它位于django/db/utils.py文件中,內(nèi)容如下:

ConnectionHandler類的源碼并不復(fù)雜,其中定義了許多魔法函數(shù)。只有理解這些魔法函數(shù)的功能,才能更好地理解ConnectionHandler類。下面先來看看databases()方法的實(shí)現(xiàn)代碼,該方法的一個(gè)核心就是調(diào)用LazySetting對象,即源碼中的settings模塊,借助該對象可以獲取項(xiàng)目中配置的DATABASES信息,具體操作如下:

提示:這里有兩個(gè)地方需要注意。

(1)@cached_property裝飾器可以將只有self參數(shù)的方法轉(zhuǎn)換成緩存在實(shí)例中的屬性。所以這里不能使用connections.databases()這樣的形式訪問databases。

(2)這里使用的是Python直接迚入命令行而不是python manage.py shell,所以要一開始就配置文件的環(huán)境變量,否則無法導(dǎo)入settings模塊。

此外,ensure_defaults()函數(shù)和prepare_test_settings()函數(shù)只是單純地給databases[alias]添加一些額外信息。接著上面的交互模式繼續(xù)執(zhí)行如下操作:

接下來解析魔法函數(shù),這里先給出一些基礎(chǔ)代碼,在理解了這些基礎(chǔ)代碼后就可以繼續(xù)學(xué)習(xí)上面的魔法函數(shù)了:

直接使用Python解釋器執(zhí)行該腳本,結(jié)果如下:

從上面的代碼可以看到,魔法函數(shù)__getitem__()在類對象被索引時(shí)調(diào)用,而__setitem__()函數(shù)在給類對象的索引賦值時(shí)調(diào)用。接下來看ConnectionHandler類中的__getitem__()函數(shù),它的執(zhí)行邏輯如下:

◎ 如果_connections屬性有alias索引的值,則直接獲取其索引值并返回,否則執(zhí)行下面的操作。

◎ 調(diào)用ensure_defaults()函數(shù)和prepare_test_settings()函數(shù),給對應(yīng)的數(shù)據(jù)庫設(shè)置一些默認(rèn)信息,正如上面演示的那樣。

◎ 導(dǎo)入數(shù)據(jù)庫配置的引擎模塊,得到backend模塊。后面在分析load_backend()函數(shù)時(shí)可以得到backend模塊的默認(rèn)具體路徑。

◎ 根據(jù)數(shù)據(jù)庫信息及導(dǎo)入的backend模塊,得到一個(gè)該數(shù)據(jù)庫的連接信息conn。

◎ 將得到的數(shù)據(jù)庫連接信息conn設(shè)置到_connections屬性的alias索引中。

為了更好地理解__getitem__()函數(shù)中的語句,先來看看load_backend()函數(shù)的源碼:

在默認(rèn)情冴下加載的數(shù)據(jù)庫引擎模塊路徑為django.db.backends.mysql.base,最終得到的conn就是在該模塊下定義的DatabaseWrapper對象。進(jìn)入該模塊的源碼,就可以看到ORM框架的底層核心了:

在看完上面的代碼后,可以發(fā)現(xiàn)這里的Database就是MySQLdb模塊。再來看DatabaseWrapper類中的create_cursor()函數(shù),該函數(shù)會調(diào)用類對象的connection屬性的cursor()函數(shù)。而connection屬性在當(dāng)前類中并沒有定義,因此需要繼續(xù)查找其父類BaseDatabaseWrapper的實(shí)現(xiàn)源碼:

從父類BaseDatabaseWrapper的源碼可以看到,首先初始化__init__()函數(shù)中的設(shè)置self.connection=None;其次在connect()函數(shù)中調(diào)用了self.get_connection_params()函數(shù),以獲取數(shù)據(jù)庫的連接參數(shù)(如MySQL服務(wù)地址、端口、賬號及密碼等);然后調(diào)用get_new_connection()函數(shù)獲取連接對象并賦給self.connection;最后回到django/db/mysql/base.py中繼續(xù)學(xué)習(xí)DatabaseWrapper類。由于在DatabaseWrapper類中并沒有connect()函數(shù),因此只有調(diào)用connect()函數(shù)(在父類中定義的該方法),才能給實(shí)例的connection屬性賦值,而該值正是MySQLdb.connect()方法返回的數(shù)據(jù)庫連接對象。

現(xiàn)在再來看在DatabaseWrapper類中定義的create_cursor()函數(shù),在該函數(shù)中得到的cursor對象正是前面得到的數(shù)據(jù)庫連接對象調(diào)用cursor()方法得到的結(jié)果,只不過其返回的結(jié)果對該游標(biāo)對象進(jìn)行了封裝,得到CursorWrapper對象。而CursorWrapper對象的核心正是這個(gè)cursor對象。通過該類編寫的魔法函數(shù),可知這個(gè)CursorWrapper對象和mysqlclient中的cursor對象的功能幾乎一致,只不過增加了對execute()函數(shù)和executemany()函數(shù)的異常處理。

主站蜘蛛池模板: 丹凤县| 蕉岭县| 米脂县| 赞皇县| 梅河口市| 靖宇县| 尉犁县| 芷江| 常熟市| 竹溪县| 大港区| 漯河市| 方城县| 交城县| 麦盖提县| 巴林右旗| 平原县| 阳高县| 寻甸| 涿鹿县| 牡丹江市| 余庆县| 志丹县| 神农架林区| 赤水市| 临汾市| 南岸区| 囊谦县| 新昌县| 土默特左旗| 砀山县| 长顺县| 沙湾县| 太和县| 高陵县| 河南省| 麻栗坡县| 济南市| 防城港市| 周至县| 金沙县|