這篇主題描述Django 對(duì)多個(gè)數(shù)據(jù)庫的支持。大部分Django 文檔假設(shè)你只和一個(gè)數(shù)據(jù)庫打交道。如果你想與多個(gè)數(shù)據(jù)庫打交道,你將需要一些額外的步驟。
在Django中使用多個(gè)數(shù)據(jù)庫的第一步是告訴Django 你將要使用的數(shù)據(jù)庫服務(wù)器。這通過使用DATABASES 設(shè)置完成。該設(shè)置映射數(shù)據(jù)庫別名到一個(gè)數(shù)據(jù)庫連接設(shè)置的字典,這是整個(gè)Django 中引用一個(gè)數(shù)據(jù)庫的方式。字典中的設(shè)置在 DATABASES 文檔中有完整描述。
你可以為數(shù)據(jù)庫選擇任何別名。然而,default這個(gè)別名具有特殊的含義。當(dāng)沒有選擇其它數(shù)據(jù)庫時(shí),Django 使用具有default 別名的數(shù)據(jù)庫。
下面是settings.py的一個(gè)示例片段,它定義兩個(gè)數(shù)據(jù)庫 —— 一個(gè)默認(rèn)的PostgreSQL 數(shù)據(jù)庫和一個(gè)叫做users的MySQL 數(shù)據(jù)庫:
DATABASES = {
'default': {
'NAME': 'app_data',
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'postgres_user',
'PASSWORD': 's3krit'
},
'users': {
'NAME': 'user_data',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'priv4te'
}
}
如果default 數(shù)據(jù)庫在你的項(xiàng)目中不合適,你需要小心地永遠(yuǎn)指定是想使用的數(shù)據(jù)庫。Django 要求default 數(shù)據(jù)庫必須定義,但是其參數(shù)字典可以保留為空如果不使用它。若要這樣做,你必須為你的所有的應(yīng)用的模型建立DATABASE_ROUTERS,包括正在使用的contrib 中的應(yīng)用和第三方應(yīng)用,以使得不會(huì)有查詢被路由到默認(rèn)的數(shù)據(jù)庫。下面是settings.py 的一個(gè)示例片段,它定義兩個(gè)非默認(rèn)的數(shù)據(jù)庫,其中default 有意保留為空:
DATABASES = {
'default': {},
'users': {
'NAME': 'user_data',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'superS3cret'
},
'customers': {
'NAME': 'customer_data',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_cust',
'PASSWORD': 'veryPriv@ate'
}
}
如果你試圖訪問在DATABASES 設(shè)置中沒有定義的數(shù)據(jù)庫,Django 將拋出一個(gè)django.db.utils.ConnectionDoesNotExist異常。
migrate 管理命令一次操作一個(gè)數(shù)據(jù)庫。默認(rèn)情況下,它在default 數(shù)據(jù)庫上操作,但是通過提供一個(gè)--database 參數(shù),你可以告訴migrate 同步一個(gè)不同的數(shù)據(jù)庫。因此,為了同步所有模型到我們示例中的所有數(shù)據(jù)庫,你將需要調(diào)用:
$ ./manage.py migrate
$ ./manage.py migrate --database=users
如果你不想每個(gè)應(yīng)用都被同步到同一臺(tái)數(shù)據(jù)庫上,你可以定義一個(gè)數(shù)據(jù)庫路由,它實(shí)現(xiàn)一個(gè)策略來控制特定模型的訪問性。
其它django-admin 命令與數(shù)據(jù)庫交互的方式與migrate相同 —— 它們都一次只操作一個(gè)數(shù)據(jù)庫,并使用--database來控制使用的數(shù)據(jù)庫。
使用多數(shù)據(jù)庫最簡(jiǎn)單的方法是建立一個(gè)數(shù)據(jù)庫路由模式。默認(rèn)的路由模式確保對(duì)象’粘滯‘在它們?cè)嫉臄?shù)據(jù)庫上(例如,從foo 數(shù)據(jù)庫中獲取的對(duì)象將保存在同一個(gè)數(shù)據(jù)庫中)。默認(rèn)的路由模式還確保如果沒有指明數(shù)據(jù)庫,所有的查詢都回歸到default數(shù)據(jù)庫中。
你不需要做任何事情來激活默認(rèn)的路由模式 —— 它在每個(gè)Django項(xiàng)目上’直接‘提供。然而,如果你想實(shí)現(xiàn)更有趣的數(shù)據(jù)庫分配行為,你可以定義并安裝你自己的數(shù)據(jù)庫路由。
數(shù)據(jù)庫路由是一個(gè)類,它提供4個(gè)方法:
db_for_read(model, **hints)
建議model類型的對(duì)象的讀操作應(yīng)該使用的數(shù)據(jù)庫。
如果一個(gè)數(shù)據(jù)庫操作能夠提供其它額外的信息可以幫助選擇一個(gè)數(shù)據(jù)庫,它將在hints字典中提供。合法的hints 的詳細(xì)信息在下文給出。
如果沒有建議,則返回None。
db_for_write(model, **hints)
建議Model 類型的對(duì)象的寫操作應(yīng)該使用的數(shù)據(jù)庫。
如果一個(gè)數(shù)據(jù)庫操作能夠提供其它額外的信息可以幫助選擇一個(gè)數(shù)據(jù)庫,它將在hints字典中提供。 合法的hints 的詳細(xì)信息在下文給出。
如果沒有建議,則返回None。
allow_relation(obj1, obj2, **hints)
如果obj1 和obj2 之間應(yīng)該允許關(guān)聯(lián)則返回True,如果應(yīng)該防止關(guān)聯(lián)則返回False,如果路由無法判斷則返回None。這是純粹的驗(yàn)證操作,外鍵和多對(duì)多操作使用它來決定兩個(gè)對(duì)象之間是否應(yīng)該允許一個(gè)關(guān)聯(lián)。
allow_migrate(db, app_label, model_name=None, **hints)
定義遷移操作是否允許在別名為db的數(shù)據(jù)庫上運(yùn)行。如果操作應(yīng)該運(yùn)行則返回True ,如果不應(yīng)該運(yùn)行則返回False,如果路由無法判斷則返回None。
位置參數(shù)app_label是正在遷移的應(yīng)用的標(biāo)簽。
大部分遷移操作設(shè)置model_name的值為正在遷移的模型的model._meta.model_name(模型的__name__ 的小寫)。對(duì)于RunPython和RunSQL 操作它的值為None,除非這兩個(gè)操作使用hint 提供它。
hints 用于某些操作來傳遞額外的信息給路由。
當(dāng)設(shè)置了model_name時(shí),hints 通常通過鍵'model'包含該模型的類。注意,它可能是一個(gè)歷史模型,因此不會(huì)有自定的屬性、方法或管理器。你應(yīng)該只依賴_meta。
這個(gè)方法還可以用來決定一個(gè)給定數(shù)據(jù)庫上某個(gè)模型的可用性。
注意,如果這個(gè)方法返回False,遷移將默默地不會(huì)在模型上做任何操作。這可能導(dǎo)致你應(yīng)用某些操作之后出現(xiàn)損壞的外鍵、表多余或者缺失。
Changed in Django 1.8:
The signature of allow_migrate has changed significantly from previous versions. See the deprecation notes for more details.
路由不必提供所有這些方法 —— 它可以省略一個(gè)或多個(gè)。如果某個(gè)方法缺失,在做相應(yīng)的檢查時(shí)Django 將忽略該路由。
Hint 由數(shù)據(jù)庫路由接收,用于決定哪個(gè)數(shù)據(jù)庫應(yīng)該接收一個(gè)給定的請(qǐng)求。
目前,唯一一個(gè)提供的hint 是instance,它是一個(gè)對(duì)象實(shí)例,與正在進(jìn)行的讀或者寫操作關(guān)聯(lián)。This might be the instance that is being saved, or it might be an instance that is being added in a many-to-many relation. In some cases, no instance hint will be provided at all. The router checks for the existence of an instance hint, and determine if that hint should be used to alter routing behavior.
數(shù)據(jù)庫路由使用DATABASE_ROUTERS 設(shè)置安裝。這個(gè)設(shè)置定義一個(gè)類名的列表,其中每個(gè)類表示一個(gè)路由,它們將被主路由(django.db.router)使用。
Django 的數(shù)據(jù)庫操作使用主路由來分配數(shù)據(jù)庫的使用。每當(dāng)一個(gè)查詢需要知道使用哪一個(gè)數(shù)據(jù)庫時(shí),它將調(diào)用主路由,并提供一個(gè)模型和一個(gè)Hint (可選)。Django 然后依次測(cè)試每個(gè)路由直至找到一個(gè)數(shù)據(jù)庫的建議。如果找不到建議,它將嘗試Hint 實(shí)例的當(dāng)前_state.db。如果沒有提供Hint 實(shí)例,或者該實(shí)例當(dāng)前沒有數(shù)據(jù)庫狀態(tài),主路由將分配default 數(shù)據(jù)庫。
只是為了示例!
這個(gè)例子的目的是演示如何使用路由這個(gè)基本結(jié)構(gòu)來改變數(shù)據(jù)庫的使用。它有意忽略一些復(fù)雜的問題,目的是為了演示如何使用路由。
如果
myapp中的任何一個(gè)模型包含與其它 數(shù)據(jù)庫之外的模型的關(guān)聯(lián),這個(gè)例子將不能工作??鐢?shù)據(jù)的關(guān)聯(lián)引入引用完整性問題,Django目前還無法處理。
Primary/replica(在某些數(shù)據(jù)庫中叫做master/slave)配置也是有缺陷的 —— 它不提供任何處理Replication lag 的解決辦法(例如,因?yàn)閷懭胪降絩eplica 需要一定的時(shí)間,這會(huì)引入查詢的不一致)。It also doesn’t consider the interaction of transactions with the database utilization strategy.
那么 —— 在實(shí)際應(yīng)用中這以為著什么?讓我們看一下另外一個(gè)配置的例子。這個(gè)配置將有幾個(gè)數(shù)據(jù)庫:一個(gè)用于auth 應(yīng)用,所有其它應(yīng)用使用一個(gè)具有兩個(gè)讀replica 的 primary/replica。下面是表示這些數(shù)據(jù)庫的設(shè)置:
DATABASES = {
'auth_db': {
'NAME': 'auth_db',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'swordfish',
},
'primary': {
'NAME': 'primary',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'spam',
},
'replica1': {
'NAME': 'replica1',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'eggs',
},
'replica2': {
'NAME': 'replica2',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'bacon',
},
}
現(xiàn)在我們將需要處理路由。首先,我們需要一個(gè)路由,它知道發(fā)送auth 應(yīng)用的查詢到auth_db:
class AuthRouter(object):
"""
A router to control all database operations on models in the
auth application.
"""
def db_for_read(self, model, **hints):
"""
Attempts to read auth models go to auth_db.
"""
if model._meta.app_label == 'auth':
return 'auth_db'
return None
def db_for_write(self, model, **hints):
"""
Attempts to write auth models go to auth_db.
"""
if model._meta.app_label == 'auth':
return 'auth_db'
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations if a model in the auth app is involved.
"""
if obj1._meta.app_label == 'auth' or \
obj2._meta.app_label == 'auth':
return True
return None
def allow_migrate(self, db, app_label, model=None, **hints):
"""
Make sure the auth app only appears in the 'auth_db'
database.
"""
if app_label == 'auth':
return db == 'auth_db'
return None
我們還需要一個(gè)路由,它發(fā)送所有其它應(yīng)用的查詢到primary/replica 配置,并隨機(jī)選擇一個(gè)replica 來讀?。?/p>
import random
class PrimaryReplicaRouter(object):
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_list = ('primary', 'replica1', 'replica2')
if obj1._state.db in db_list and obj2._state.db in db_list:
return True
return None
def allow_migrate(self, db, app_label, model=None, **hints):
"""
All non-auth models end up in this pool.
"""
return True
最后,在設(shè)置文件中,我們添加如下內(nèi)容(替換path.to.為該路由定義所在的真正路徑):
DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.PrimaryReplicaRouter']
路由處理的順序非常重要。路由的查詢將按照DATABASE_ROUTERS設(shè)置中列出的順序進(jìn)行。在這個(gè)例子中,AuthRouter在PrimaryReplicaRouter之前處理,因此auth中的模型的查詢處理在其它模型之前。如果DATABASE_ROUTERS設(shè)置按其它順序列出這兩個(gè)路由,PrimaryReplicaRouter.allow_migrate() 將先處理。PrimaryReplicaRouter 中實(shí)現(xiàn)的捕獲所有的查詢,這意味著所有的模型可以位于所有的數(shù)據(jù)庫中。
建立這個(gè)配置后,讓我們運(yùn)行一些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')
Django 還提供一個(gè)API,允許你在你的代碼中完全控制數(shù)據(jù)庫的使用。人工指定的數(shù)據(jù)庫的優(yōu)先級(jí)高于路由分配的數(shù)據(jù)庫。
你可以在QuerySet“鏈”的任意節(jié)點(diǎn)上為QuerySet選擇數(shù)據(jù)庫 。只需要在QuerySet上調(diào)用using()就可以讓QuerySet使用一個(gè)指定的數(shù)據(jù)庫。
using() 接收單個(gè)參數(shù):你的查詢想要運(yùn)行的數(shù)據(jù)庫的別名。例如:
>>> # This will run on the 'default' database.
>>> Author.objects.all()
>>> # So will this.
>>> Author.objects.using('default').all()
>>> # This will run on the 'other' database.
>>> Author.objects.using('other').all()
對(duì)Model.save()使用using 關(guān)鍵字來指定數(shù)據(jù)應(yīng)該保存在哪個(gè)數(shù)據(jù)庫。
例如,若要保存一個(gè)對(duì)象到legacy_users 數(shù)據(jù)庫,你應(yīng)該使用:
>>> my_object.save(using='legacy_users')
如果你不指定using,save()方法將保存到路由分配的默認(rèn)數(shù)據(jù)庫中。
如果你已經(jīng)保存一個(gè)實(shí)例到一個(gè)數(shù)據(jù)庫中,你可能很想使用save(using=...) 來遷移該實(shí)例到一個(gè)新的數(shù)據(jù)庫中。然而,如果你不使用正確的步驟,這可能導(dǎo)致意外的結(jié)果。
考慮下面的例子:
>>> p = Person(name='Fred')
>>> p.save(using='first') # (statement 1)
>>> p.save(using='second') # (statement 2)
在statement 1中,一個(gè)新的Person 對(duì)象被保存到 first 數(shù)據(jù)庫中。此時(shí)p沒有主鍵,所以Django 發(fā)出一個(gè)SQL INSERT語句。這會(huì)創(chuàng)建一個(gè)主鍵,且Django 將此主鍵賦值給p。
當(dāng)保存在statement 2中發(fā)生時(shí),p已經(jīng)具有一個(gè)主鍵,Django 將嘗試在新的數(shù)據(jù)庫上使用該主鍵。如果該主鍵值在second 數(shù)據(jù)庫中沒有使用,那么你不會(huì)遇到問題 —— 該對(duì)象將被復(fù)制到新的數(shù)據(jù)庫中。
然而,如果p 的主鍵在second數(shù)據(jù)庫上已經(jīng)在使用second 數(shù)據(jù)庫中的已經(jīng)存在的對(duì)象將在p保存時(shí)被覆蓋。
你可以用兩種方法避免這種情況。首先,你可以清除實(shí)例的主鍵。如果一個(gè)對(duì)象沒有主鍵,Django 將把它當(dāng)做一個(gè)新的對(duì)象,這將避免second數(shù)據(jù)庫上數(shù)據(jù)的丟失:
>>> p = Person(name='Fred')
>>> p.save(using='first')
>>> p.pk = None # Clear the primary key.
>>> p.save(using='second') # Write a completely new object.
第二種方法是使用force_insert 選項(xiàng)來save()以確保Django 使用一個(gè)INSERT SQL:
>>> p = Person(name='Fred')
>>> p.save(using='first')
>>> p.save(using='second', force_insert=True)
這將確保名稱為Fred 的Person在兩個(gè)數(shù)據(jù)庫上具有相同的主鍵。在你試圖保存到second數(shù)據(jù)庫,如果主鍵已經(jīng)在使用,將會(huì)引拋出發(fā)一個(gè)錯(cuò)誤。
默認(rèn)情況下,刪除一個(gè)已存在對(duì)象的調(diào)用將在與獲取對(duì)象時(shí)使用的相同數(shù)據(jù)庫上執(zhí)行:
>>> u = User.objects.using('legacy_users').get(username='fred')
>>> u.delete() # will delete from the `legacy_users` database
要指定刪除一個(gè)模型時(shí)使用的數(shù)據(jù)庫,可以對(duì)Model.delete()方法使用using 關(guān)鍵字參數(shù)。這個(gè)參數(shù)的工作方式與save()的using關(guān)鍵字參數(shù)一樣。
例如,你正在從legacy_users 數(shù)據(jù)庫到new_users 數(shù)據(jù)庫遷移一個(gè)User ,你可以使用這些命令:
>>> user_obj.save(using='new_users')
>>> user_obj.delete(using='legacy_users')
在管理器上使用db_manager()方法來讓管理器訪問非默認(rèn)的數(shù)據(jù)庫。
例如,你有一個(gè)自定義的管理器方法,它訪問數(shù)據(jù)庫時(shí)候用 ——User.objects.create_user()。因?yàn)?code>create_user()是一個(gè)管理器方法,不是一個(gè)QuerySet方法,你不可以使用User.objects.using('new_users').create_user()。(create_user() 方法只能在User.objects上使用,而不能在從管理器得到的QuerySet上使用)。解決辦法是使用db_manager(),像這樣:
User.objects.db_manager('new_users').create_user(...)
db_manager() 返回一個(gè)綁定在你指定的數(shù)據(jù)上的一個(gè)管理器。
如果你正在覆蓋你的管理器上的get_queryset(),請(qǐng)確保在其父類上調(diào)用方法(使用super())或者正確處理管理器上的_db屬性(一個(gè)包含將要使用的數(shù)據(jù)庫名稱的字符串)。
例如,如果你想從get_queryset 方法返回一個(gè)自定義的 QuerySet 類,你可以這樣做:
class MyManager(models.Manager):
def get_queryset(self):
qs = CustomQuerySet(self.model)
if self._db is not None:
qs = qs.using(self._db)
return qs
Django 的管理站點(diǎn)沒有對(duì)多數(shù)據(jù)庫的任何顯式的支持。如果你給數(shù)據(jù)庫上某個(gè)模型提供的管理站點(diǎn)不想通過你的路由鏈指定,你將需要編寫自定義的ModelAdmin類用來將管理站點(diǎn)導(dǎo)向一個(gè)特殊的數(shù)據(jù)庫。
ModelAdmin 對(duì)象具有5個(gè)方法,它們需要定制以支持多數(shù)據(jù)庫:
class MultiDBModelAdmin(admin.ModelAdmin):
# A handy constant for the name of the alternate database.
using = 'other'
def save_model(self, request, obj, form, change):
# Tell Django to save objects to the 'other' database.
obj.save(using=self.using)
def delete_model(self, request, obj):
# Tell Django to delete objects from the 'other' database
obj.delete(using=self.using)
def get_queryset(self, request):
# Tell Django to look for objects on the 'other' database.
return super(MultiDBModelAdmin, self).get_queryset(request).using(self.using)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
# Tell Django to populate ForeignKey widgets using a query
# on the 'other' database.
return super(MultiDBModelAdmin, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
# Tell Django to populate ManyToMany widgets using a query
# on the 'other' database.
return super(MultiDBModelAdmin, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)
這里提供的實(shí)現(xiàn)實(shí)現(xiàn)了一個(gè)多數(shù)據(jù)庫策略,其中一個(gè)給定類型的所有對(duì)象都將保存在一個(gè)特定的數(shù)據(jù)庫上(例如,所有的User保存在other 數(shù)據(jù)庫中)。如果你的多數(shù)據(jù)庫的用法更加復(fù)雜,你的ModelAdmin將需要反映相應(yīng)的策略。
Inlines 可以用相似的方式處理。它們需要3個(gè)自定義的方法:
class MultiDBTabularInline(admin.TabularInline):
using = 'other'
def get_queryset(self, request):
# Tell Django to look for inline objects on the 'other' database.
return super(MultiDBTabularInline, self).get_queryset(request).using(self.using)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
# Tell Django to populate ForeignKey widgets using a query
# on the 'other' database.
return super(MultiDBTabularInline, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
# Tell Django to populate ManyToMany widgets using a query
# on the 'other' database.
return super(MultiDBTabularInline, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)
一旦你寫好你的模型管理站點(diǎn)的定義,它們就可以使用任何Admin實(shí)例來注冊(cè):
from django.contrib import admin
# Specialize the multi-db admin objects for use with specific models.
class BookInline(MultiDBTabularInline):
model = Book
class PublisherAdmin(MultiDBModelAdmin):
inlines = [BookInline]
admin.site.register(Author, MultiDBModelAdmin)
admin.site.register(Publisher, PublisherAdmin)
othersite = admin.AdminSite('othersite')
othersite.register(Publisher, MultiDBModelAdmin)
這個(gè)例子建立兩個(gè)管理站點(diǎn)。在第一個(gè)站點(diǎn)上,Author 和 Publisher 對(duì)象被暴露出來;Publisher 對(duì)象具有一個(gè)表格的內(nèi)聯(lián),顯示該出版社出版的書籍。第二個(gè)站點(diǎn)只暴露Publishers,而沒有內(nèi)聯(lián)。
如果你正在使用多個(gè)數(shù)據(jù)庫,你可以使用django.db.connections來獲取特定數(shù)據(jù)庫的連接(和游標(biāo)):django.db.connections是一個(gè)類字典對(duì)象,它允許你使用別名來獲取一個(gè)特定的連接:
from django.db import connections
cursor = connections['my_db_alias'].cursor()
Django 目前不提供跨多個(gè)數(shù)據(jù)庫的外鍵或多對(duì)多關(guān)系的支持。如果你使用一個(gè)路由來路由分離到不同的數(shù)據(jù)庫上,這些模型定義的任何外鍵和多對(duì)多關(guān)聯(lián)必須在單個(gè)數(shù)據(jù)庫的內(nèi)部。
這是因?yàn)橐猛暾缘脑?。為了保持兩個(gè)對(duì)象之間的關(guān)聯(lián),Django 需要知道關(guān)聯(lián)對(duì)象的主鍵是合法的。如果主鍵存儲(chǔ)在另外一個(gè)數(shù)據(jù)庫上,判斷一個(gè)主鍵的合法性不是很容易。
如果你正在使用Postgres、Oracle或者M(jìn)ySQ 的InnoDB,這是數(shù)據(jù)庫完整性級(jí)別的強(qiáng)制要求 —— 數(shù)據(jù)庫級(jí)別的主鍵約束防止創(chuàng)建不能驗(yàn)證合法性的關(guān)聯(lián)。
然而,如果你正在使用SQLite 或MySQL的MyISAM 表,則沒有強(qiáng)制性的引用完整性;結(jié)果是你可以‘偽造’跨數(shù)據(jù)庫的外鍵。但是Django 官方不支持這種配置。
有幾個(gè)Contrib 應(yīng)用包含模型,其中一些應(yīng)用相互依賴。因?yàn)榭鐢?shù)據(jù)庫的關(guān)聯(lián)是不可能的,這對(duì)你如何在數(shù)據(jù)庫之間劃分這些模型帶來一些限制:
contenttypes.ContentType、sessions.Session和sites.Site 可以存儲(chǔ)在分開存儲(chǔ)在不同的數(shù)據(jù)庫中,只要給出合適的路由auth模型 —— User、Group和Permission —— 關(guān)聯(lián)在一起并與ContentType關(guān)聯(lián),所以它們必須與ContentType存儲(chǔ)在相同的數(shù)據(jù)庫中。admin依賴auth,所以它們的模型必須與auth在同一個(gè)數(shù)據(jù)庫中。flatpages和redirects依賴sites,所以它們必須與sites在同一個(gè)數(shù)據(jù)庫中。另外,一些對(duì)象在migrate在數(shù)據(jù)庫中創(chuàng)建一張表后自動(dòng)創(chuàng)建:
Site,ContentType(包括沒有存儲(chǔ)在同一個(gè)數(shù)據(jù)庫中的模型),Permission (包括不是存儲(chǔ)在同一個(gè)數(shù)據(jù)庫中的模型)。對(duì)于常見的多數(shù)據(jù)庫架構(gòu),將這些對(duì)象放在多個(gè)數(shù)據(jù)庫中沒有什么用處。常見的數(shù)據(jù)庫架構(gòu)包括primary/replica 和連接到外部的數(shù)據(jù)庫。因此,建議寫一個(gè)數(shù)據(jù)庫路由,它只允許同步這3個(gè)模型到一個(gè)數(shù)據(jù)中。對(duì)于不需要將表放在多個(gè)數(shù)據(jù)庫中的Contrib 應(yīng)用和第三方應(yīng)用,可以使用同樣的方法。
警告
如果你將
Content Types同步到多個(gè)數(shù)據(jù)庫中,注意它們的主鍵在數(shù)據(jù)庫之間可能不一致。這可能導(dǎo)致數(shù)據(jù)損壞或數(shù)據(jù)丟失。
譯者:Django 文檔協(xié)作翻譯小組,原文:Multiple databases。
本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請(qǐng)保留作者署名和文章出處。
Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。