Django ORM
Basic information
- Django version: 2.1
Introduction
下述是一個例子,查詢在User table中所有User的資訊
Model Manager
User變量是settings.AUTH_USER_MODEL中指定的Model,預設為AbstractUser @django/contrib/auth/models.py。
其父類AbstractBaseUser @django/contrib/auth/base_user.py 為models.Model;換言之,AbstractUser本質上是models.Model。
在AbstractUser中明確指定objects為UserManager class。
@django/contrib/auth/models.py
@django/contrib/auth/base_user.py
但若在其他自定義的Model子類中,不指定objects,那麼在使用Model時,objects會是什麼呢?
查看models.Model @django/db/models/base.py
可以得知若沒有指定objects,會在ModelBase的_prepare()方法中使用Manager @django/db/models/manager.py作為預設的object,及為manager變量
同時Manager class也為上述UserManager的父類
@django/db/models/base.py
@django/db/models/manager.py
透過上述的代碼追蹤,可以得知:
- Model都有objects變量,且其一定是Manager的子類
Manager Object Initialization Flow
Manager會繼承一個動態生成的class,而BaseManager.from_queryset(QuerySet)就是返回動態生成class的方法。
在from_queryset中使用Python type方法,初始化名稱為class_name變量值的Class object,以BaseManager為父類,並透過_get_queryset_methods(…)方法將QuerySet添加到自身中
@django/db/models/manager.py
透過上述的代碼追蹤,可以得知:
- 創建Manager時,使用Meta class技巧,將QuerySet的方法添加到Manager中,方便使用者直接透過Manager使用QuerySet所提供的方法。
Model Class Diagram
QuerySet
上述代碼追蹤到了objects,接著下面說明.all(…)
操作Manager的方法,其實就是操作QuerySet的方法,下面使用常見的all()方法追蹤QuerySet的流程。
調用all()方法時,QuerySet會連續調用內部方法到_clone(),_clone()頭部註釋已經說明,此方法會回傳當前QuerySet的副本。
故執行all()方法時,其實是拿到當前QuerySet的副本,沒有與真實的Database做任何交互。
@django/db/models/query.py
在外部使用者透過for loop對all()方法的回傳值進行操作時,會調用QuerySet的__iter__(),QuerySet就會與Database進行交互。
__iter__()方法會調用內部方法_fetch_all(),_fetch_all()透過self._iterable_class(self)進行Database查詢。
self._iterable_class在QuerySet初始化時指定為ModelIterable。
接著使用list(…)會調用ModelIterable的__iter__()。
Query Class Diagram
ModelIterable Class
下述以ModelIterable為主軸進行追蹤
- compiler = queryset.query.get_compiler(using=db)
- 使用queryset.query.get_compiler(using=db)設定compiler
- queryset.query在QuerySet初始化時已經指定為sql.Query(self.model),其為Query class @@django/db/models/sql/query.py
- 故queryset.query.get_compiler(…)方法就是Query的get_compiler(…)
- 在get_compiler(…)內部使用connections[using]設定connection變量
- connections @django/db/__init__.py,真實為ConnectionHandler @django/db/utils.py
- connections[using]會調用ConnectionHandler的__getitem__()
- databases變量值為Django settings的DATABASES值,為Dictionary類型,alias變量值為’default’
- backend變量則為’ENGINE’指定的Class object,例如’django.db.backends.mysql’
- conn變量值則為backend對應的DatabaseWrapper instance,例如DatabaseWrapper @django/db/backends/mysql/base.py
- 逐層返回到Query的get_compiler(…)方法,執行connection.ops.compiler(…)(…)
- connection即是DatabaseWrapper
- connection.ops則是在DatabaseWrapper的父類BaseDatabaseWrapper @django/db/backends/base/base.py設值,來源為DatabaseOperations @django/db/backends/mysql/operations.py
- connection.ops.compiler(…)為DatabaseOperations父類BaseDatabaseOperations @django/db/backends/base/operations.py的方法,其方法會回傳子類宣告compiler_module中的compiler_name class,此時compiler_module的值為”django.db.backends.mysql.compiler”,compiler_name的值為’SQLCompiler’,故回傳值為SQLCompiler @django/db/backends/mysql/compiler.py
- connection.ops.compiler(…)(…)就是SQLCompiler(…),即是對SQLCompiler instance進行初始化
- 使用queryset.query.get_compiler(using=db)設定compiler
- compiler = queryset.query.get_compiler(using=db),根據上述追蹤,compiler即是SQLCompiler instance
- results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
- 調用SQLCompiler父類SQLCompiler @django/db/models/sql/compiler.py 的execute_sql(…)
- SQLCompiler的execute_sql(…),這邊關注cursor = self.connection.cursor()
- self.connction為初始化時設定,即是在Query的get_compiler的connection傳入,當時的connection為DatabaseWrapper instance
- 所以self.connection.cursor()則是調用DatabaseWrapper instance的cursor()方法
- DatabaseWrapper的父類BaseDatabaseWrapper的cursor()方法透過一連串的自身與子類方法調用,最後會調用到自身的connect()方法
- get_connection_params()是讀取Djangp Settings的Database配置信息
- get_new_connection()則是使用第三方的MySQLdb lib與實體Database進行交互
- 上述兩個方法會將self.connection創建好,則是MySQLdb.connect(…)回傳的instance
- DatabaseWrapper的create_cursor方法
- 呼叫self.connection的cursor()方法,即是Database.connect(…).cursor()結果
- 回到SQLCompiler的execute_sql(…),關注到result = cursor_iter(…)
- 在cursor_iter中,會調用上述的cursor的fetchmany(…)方法,其會返回透過第三方MySQLdb lib到MySQL查詢獲取的結果
- cursor_iter的第二個入參sentinel則是self.connection.features.empty_fetchmany_value,對應的是DatabaseWrapper instance的features.empty_fetchmany_value,則是DatabaseFeatures @django/db/backends/mysql/features.py的empty_fetchmany_value,其值為()
- 最終將查詢結果返回給SQLCompiler的execute_sql(…)
- ModelIterable在execute_sql(…)得到真實Database回傳的數據後,會透過model_cls.from_db(…)將取回的數據,逐行包裝成Model instance,此時的model_cls為ModelBase @django/db/models/base.py
上述即是User.objects.all()以及使用For loop的整體流程追蹤。
上述提到的相關代碼如下:
@django/db/models/query.py
@django/db/models/sql/query.py
@django/db/init.py
@django/db/utils.py
@django/db/backends/mysql/base.py
@django/db/backends/base/base.py
@django/db/backends/mysql/features.py
@django/db/backends/mysql/operations.py
@django/db/backends/base/operations.py
@django/db/backends/mysql/compiler.py
@django/db/models/sql/compiler.py