W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
使用多數(shù)據(jù)庫最簡單的方式就是設(shè)置數(shù)據(jù)庫路由方案。默認路由方案確保對象對原始數(shù)據(jù)庫保持粘性(比如,從 ?foo
?數(shù)據(jù)庫檢索到的對象將被保持到同一個數(shù)據(jù)庫)。默認路由方案確保當數(shù)據(jù)庫沒有指定時,所有查詢回退到 ?default
?數(shù)據(jù)庫。
你無需執(zhí)行任何操作來激活默認路由——在每個 Django 項目上是開箱即用的。然而,如果想實現(xiàn)更多有趣的數(shù)據(jù)庫分配行為,可以定義和安裝自己的數(shù)據(jù)庫路由。
數(shù)據(jù)庫路由是一個類,它提供四種方法:
建議用于讀取?model
?類型對象的數(shù)據(jù)庫。
如果數(shù)據(jù)庫操作可以提供有助于選擇數(shù)據(jù)庫的任何附加信息,它將在 ?hints
?中提供。
如果沒有建議,則返回 ?None
?。
建議用于寫入?model
?類型對象的數(shù)據(jù)庫。
如果數(shù)據(jù)庫操作可以提供有助于選擇數(shù)據(jù)庫的任何附加信息,它將在 ?hints
?中提供。
如果沒有建議,則返回 ?None
?。
如果允許 ?obj1
?和 ?obj2
?之間的關(guān)系,返回 ?True
?。如果阻止關(guān)系,返回 ?False
?,或如果路由沒意見,則返回 ?None
?。這純粹是一種驗證操作,由外鍵和多對多操作決定是否應(yīng)該允許關(guān)系。
如果沒有路由有意見(比如所有路由返回 ?None
?),則只允許同一個數(shù)據(jù)庫內(nèi)的關(guān)系。
決定是否允許遷移操作在別名為 ?db
?的數(shù)據(jù)庫上運行。如果操作運行,那么返回 ?True
?,如果沒有運行則返回 ?False
?,或路由沒有意見則返回 ?None
?。
?app_label
?參數(shù)是要遷移的應(yīng)用程序的標簽。
?model_name
?由大部分遷移操作設(shè)置來要遷移的模型的 ?model._meta.model_name
? (模型 ?__name__
? 的小寫版本) 的值。 對于 RunPython 和 RunSQL 操作的值是 ?None
?,除非它們提示要提供它。
?hints
通過某些操作來向路由傳達附加信息。
當設(shè)置 ?model_name
?,?hints
? 通常包含 ?model
?下的模型類。注意它可能是 ?historical model
? ,因此沒有任何自定義屬性,方法或管理器。你應(yīng)該只能依賴 ?_meta
? 。
這個方法也可以用于確定給定數(shù)據(jù)庫上模型的可用性。
?makemigrations
?會在模型變動時創(chuàng)建遷移,但如果 ?allow_migrate()
? 返回 ?False
?,任何針對 ?model_name
? 的遷移操作會在運行 ?migrate
的時候跳過。對于已經(jīng)遷移過的模型,改變 ?allow_migrate()
? 的行為,可能會破壞主鍵,格外表或丟失的表。當 ?makemigrations
?核實遷移歷史,它跳過不允許遷移的 app 的數(shù)據(jù)庫。
路由不是必須提供所有這些方法——它也許省略它們中的一個或多個。如果某個方法被省略,Django會在執(zhí)行相關(guān)檢查時候,跳過這個路由。
數(shù)據(jù)庫路由 ?DATABASE_ROUTERS
?配置安裝。這個配置定義類名列表,每個類名指定了主路由?django.db.router
?應(yīng)使用的路由。
Django 的數(shù)據(jù)庫操作使用主路由來分配數(shù)據(jù)庫使用。每當查詢需要知道正在使用哪個數(shù)據(jù)庫時,它會調(diào)用主路由,提供一個模型和提示(如果可用的話),然后 Django 會依次嘗試每個路由直到找到數(shù)據(jù)庫。如果沒有找到,它試著訪問提示實例的當前 ?instance._state.db
? 。如果沒有提供提示實例,或者 ?instance._state.db
? 為 ?None
?,主路由將分配默認數(shù)據(jù)庫。
例如我們有一些數(shù)據(jù)庫:一個 ?auth
?應(yīng)用,和其他應(yīng)用使用帶有兩個只讀副本的主/副設(shè)置。以下是指定這些數(shù)據(jù)庫的設(shè)置:
DATABASES = {
'default': {},
'auth_db': {
'NAME': 'auth_db_name',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'swordfish',
},
'primary': {
'NAME': 'primary_name',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'spam',
},
'replica1': {
'NAME': 'replica1_name',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'eggs',
},
'replica2': {
'NAME': 'replica2_name',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'bacon',
},
}
現(xiàn)在需要處理路由。首先需要一個將 ?auth
?和 ?contenttypes app
? 的查詢發(fā)送到 ?auth_db
?的路由(?auth
?模型已經(jīng)關(guān)聯(lián)了 ?ContentType
?,因此它們必須保存在同一個數(shù)據(jù)庫里):
class AuthRouter:
"""
A router to control all database operations on models in the
auth and contenttypes applications.
"""
route_app_labels = {'auth', 'contenttypes'}
def db_for_read(self, model, **hints):
"""
Attempts to read auth and contenttypes models go to auth_db.
"""
if model._meta.app_label in self.route_app_labels:
return 'auth_db'
return None
def db_for_write(self, model, **hints):
"""
Attempts to write auth and contenttypes models go to auth_db.
"""
if model._meta.app_label in self.route_app_labels:
return 'auth_db'
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations if a model in the auth or contenttypes apps is
involved.
"""
if (
obj1._meta.app_label in self.route_app_labels or
obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Make sure the auth and contenttypes apps only appear in the
'auth_db' database.
"""
if app_label in self.route_app_labels:
return db == 'auth_db'
return None
我們也需要一個發(fā)送所有其他應(yīng)用到主/副配置的路由,并且隨機選擇一個副本來讀?。?/p>
import random
class PrimaryReplicaRouter:
def db_for_read(self, model, **hints):
"""
Reads go to a randomly-chosen replica.
"""
return random.choice(['replica1', 'replica2'])
def db_for_write(self, model, **hints):
"""
Writes always go to primary.
"""
return 'primary'
def allow_relation(self, obj1, obj2, **hints):
"""
Relations between objects are allowed if both objects are
in the primary/replica pool.
"""
db_set = {'primary', 'replica1', 'replica2'}
if obj1._state.db in db_set and obj2._state.db in db_set:
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
All non-auth models end up in this pool.
"""
return True
最后,在配置文件中,我們添加下面的代碼(用定義路由器的模塊的實際 Python 路徑替換 ?path.to.
? ):
DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.PrimaryReplicaRouter']
處理路由的順序非常重要。路由將按照 ?DATABASE_ROUTERS
?里設(shè)置的順序查詢。在這個例子里, ?AuthRouter
?將在 ?PrimaryReplicaRouter
?前處理,因此,在做出其他決定之前,先處理與 ?auth
?相關(guān)的模型。如果 ?DATABASE_ROUTERS
?設(shè)置在其他順序里列出兩個路由,?PrimaryReplicaRouter.allow_migrate()
? 將首先處理。?PrimaryReplicaRouter
? 實現(xiàn)的特性意味著所有模型可用于所有數(shù)據(jù)庫。
安裝好這個設(shè)置,并按照 同步數(shù)據(jù)庫 的要求遷移所有的數(shù)據(jù)庫,讓我們運行一些 Django 代碼:
>>> # This retrieval will be performed on the 'auth_db' database
>>> fred = User.objects.get(username='fred')
>>> fred.first_name = 'Frederick'
>>> # This save will also be directed to 'auth_db'
>>> fred.save()
>>> # These retrieval will be randomly allocated to a replica database
>>> dna = Person.objects.get(name='Douglas Adams')
>>> # A new object has no database allocation when created
>>> mh = Book(title='Mostly Harmless')
>>> # This assignment will consult the router, and set mh onto
>>> # the same database as the author object
>>> mh.author = dna
>>> # This save will force the 'mh' instance onto the primary database...
>>> mh.save()
>>> # ... but if we re-retrieve the object, it will come back on a replica
>>> mh = Book.objects.get(title='Mostly Harmless')
這個例子定義了一個路由來處理與來自 ?auth
?應(yīng)用的模型交互,其他路由處理與所以其他應(yīng)用的交互。如果 ?default
?為空,并且不想定義一個全能數(shù)據(jù)庫來處理所有未指定的應(yīng)用,那么路由必須在遷移之前處理 ?INSTALLED_APPS
?的所有應(yīng)用名。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: