當前位置:成語大全網 - 新華字典 - 如何在django中使用多個數據庫

如何在django中使用多個數據庫

使用多個數據庫

New in Django 1.2: Please, see the release notes

大多數其他文檔都假設使用單壹數據庫,本文主要討論如何在 Django 中使用多個數據庫。 使用多個數據庫,要增加壹些步驟。

定義妳的數據庫

使用多數據庫的第壹步是通過 DATABASES 設置要使用的數據庫服務。這個 設置用於映射數據庫別名和特定的聯結設置字典,這是 Django 定義數據庫壹貫的手法。 字典內部的設置參見 DATABASES 文檔。

數據庫可以使用任何別名,但是 default 有特殊意義。當沒有選擇其他數據庫時, Django 總是使用別名為 default 的數據庫。因此,如果妳沒有定義壹個名為 default 的數據庫時,妳應當小心了,在使用數據庫前要指定妳想用的數據庫。

以下是壹個定義兩個數據庫的 settings.py 代碼片斷。定義了壹個缺省的 PostgreSQL 數據庫和壹個名為 users 的 MySQL 數據庫:

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' } }

如果妳嘗試訪問 DATABASES 設置中沒有定義的數據庫, Django 會拋出壹個 django.db.utils.ConnectionDoesNotExist異常。

同步妳的數據庫

syncdb 管理命令壹次只操作壹個數據庫。缺省情況下,它操作 default 數據庫。但是加上 --database 參數,妳可以讓 syncdb 同步不同的 數據庫。所以要同步我們例子中的所有數據庫的所有模型可以使用如下命令:

$ ./manage.py syncdb

$ ./manage.py syncdb --database=users

如果妳不是同步所有的程序到同壹個數據庫中,妳可定義壹個 數據庫路由 來為指定的模型實施特定的控制 策略。

如果妳要精細地控制同步,那麽還有壹種方式是修改 sqlall 的輸出,手工在 數據庫中執行命令,命令如下:

$ ./manage.py sqlall sales | ./manage.py dbshell

使用其他管理命令

其他操作數據庫的 django-admin.py 命令與 syncdb 類似,他們壹次只 操作壹個數據庫,使用 --database 來控制使用哪個數據庫。

自動數據庫路由

使用多數據庫最簡單的方法是設置壹個數據庫路由方案。缺省的路由方案確保對象 “緊貼”其原本的數據庫(例如:壹個對象從哪個數據庫取得,就保存回哪個數據庫)。 缺省的路由方案還確保如果壹個數據庫沒有指定,所有的查詢都會作用於 缺省 數據 庫。

妳不必為啟動缺省路由方案作任何事,因為它是“開箱即用”的。但是,如果妳要執行 壹些更有趣的數據庫分配行為的話,妳可以定義並安裝妳自己的數據庫路由。

數據庫路由

壹個數據庫路由是壹個類,這個類最多有四個方法:

db_for_read(model, **hints)

建議 model 對象寫操作時使用的數據庫。

如果壹個數據庫操作可以提供對選擇數據庫有用的附加信息,那麽可以通過 hints 字典提供。詳見 下文 。

如果沒有建議則返回 None 。

db_for_write(model, **hints)

建議 model 對象讀操作時使用的數據庫。

如果壹個數據庫操作可以提供對選擇數據庫有用的附加信息,那麽可以通過 hints 字典提供。詳見 下文 。

如果沒有建議則返回 None 。

allow_relation(obj1, obj2, **hints)

當 obj1 和 obj2 之間允許有關系時返回 True ,不允許時返回 False ,或者沒有 意見時返回 None 。這是壹個純粹的驗證操作,用於外鍵和多對多操作中,兩個對象 的關系是否被允許。

allow_syncdb(db, model)

決定 model 是否可以和 db 為別名的數據庫同步。如果可以返回 True , 如果不可以返回 False ,或者沒有意見時返回 None 。這個方法用於決定壹個給定 數據庫的模型是否可用。

壹個路由不必提供 所有 這些方法,可以省略其中壹個或多個。如果其中壹個方法被 省略了,那麽 Django 會在執行相關檢查時跳過相應路由。

提示參數

數據庫路由接收的“提示”參數可用於決定哪個數據庫應當接收壹個給定的請求。

目前,唯壹可以提供的提示參數是 實例 ,即壹個與讀寫操作相關的對象的實例。 可以是壹個已保存的對象的實例,也可以是壹個多對多關系中添加的實例。在某些情況下, 也可能沒有對象的實例可以提供。路由會檢查提示實例是否存在,並相應地決定是否改變 路由行為。

使用路由

數據庫路由使用 DATABASE_ROUTERS 設置來安裝。這個設置定義壹個類名稱 列表,每個類定義壹個用於主路由 (django.db.router) 的路由。

主路由用於 Django 分配數據庫操作。當壹個查詢想要知道使用哪個數據庫時,會提供 壹個模型和壹個提示(如果有的話),並調用主路由。

Django 就會按次序嘗試每個路由,

直到找到合適的路由建議。如果找不到路由建議就會嘗試實例提示的當前的 _state.db 。如果沒有提供路由提示,或者實例沒有當前數據庫狀態,那麽

主路由會 分配 缺省 數據庫。

壹個例子

僅用於示例目的!

這個例子僅用於展示路由如何改變數據庫的使用。本例有意忽略了壹些復雜的東西以 便於更好的展示路由是如何工作的。

如果任何壹個 myapp 中的模型包含與 另壹個 數據庫中模型的關系時,本例 是無效的。參見 跨數據庫關系壹節中介紹 的 Django 引用完整性問題。

本例的主/從配置也是有缺陷的:它沒有處理復制延時(比如因為把寫操作傳遞給從 數據庫耗費時間而產生的查詢不壹致),也沒有考慮與數據庫使用策略的交互作用。

那麽,這個例子有什麽用呢?本例僅用於演示壹個 myapp 存在於 other 數據庫, 所有其他模型之間是主/從關系,且存在於 master 、 slave1 和 slave2 數據庫。本例使用了兩個路由:

class MyAppRouter(object): """ 壹個控制 myapp 應用中模型的 所有數據庫操作的路由 """ def db_for_read(self, model, **hints): "myapp 應用中模型的操作指向 'other'" if model._meta.app_label == 'myapp': return 'other' return None def db_for_write(self, model, **hints): "myapp 應用中模型的操作指向 'other'" if model._meta.app_label == 'myapp': return 'other' return None def allow_relation(self, obj1, obj2, **hints): " 如果包含 myapp 應用中的模型則允許所有關系 " if obj1._meta.app_label == 'myapp' or obj2._meta.app_label == 'myapp': return True return None def allow_syncdb(self, db, model): " 確保 myapp 應用只存在於 'other' 數據庫 " if db == 'other': return model._meta.app_label == 'myapp' elif model._meta.app_label == 'myapp': return False return None class MasterSlaveRouter(object): """ 壹個設置簡單主/從定義 的路由 """ def db_for_read(self, model, **hints): " 所有讀操作指向壹個隨機的從數據庫 " return random.choice(['slave1','slave2']) def db_for_write(self, model, **hints): " 所有寫操作指向主數據庫 " return 'master' def allow_relation(self, obj1, obj2, **hints): " 允許數據庫池中的兩個對象間的任何關系 " db_list = ('master','slave1','slave2') if obj1._state.db in db_list and obj2._state.db in db_list: return True return None def allow_syncdb(self, db, model): " 顯示地放置所有數據庫中的模型 " return True

然後在妳的設置文件增加如下內容(把 path.to. 替換為妳定義路由的模型的路徑 ):

DATABASE_ROUTERS = ['path.to.MyAppRouter', 'path.to.MasterSlaveRouter']

這個設置中,路由的順序是很重要的,因為查詢時是按這個設置中的順序依次查詢的。上 例中, MyAppRouter 先於MasterSlaveRouter ,因此, myapp 中的模型就 優先於其他模型。如果 DATABASE_ROUTERS 設置中兩個路由的順序變換了, 那麽 MasterSlaveRouter.allow_syncdb() 會優先執行。因為 MasterSlaveRouter 是 包羅萬象的,這樣就會導致所有模型可以使用所有數據庫。

設置好之後讓我們來運行壹些代碼:

>>> # 從 'credentials' 數據庫獲得數據 >>> fred = User.objects.get(username='fred') >>> fred.first_name = 'Frederick' >>> # 保存到 'credentials' 數據庫 >>> fred.save() >>> # 隨機從從數據庫獲得數據 >>> dna = Person.objects.get(name='Douglas Adams') >>> # 新對象創建時還沒有分配數據庫 >>> mh = Book(title='Mostly Harmless') >>> # 這個賦值會向路由發出請求,並把 mh 的數據庫設置為與 author 對象同樣的 >>> # 數據庫 >>> mh.author = dna >>> # 這會強制 'mh' 實例使用主數據庫... >>> mh.save() >>> # ... 但如果我們重新獲取對象,就會從從數據庫中獲取 >>> mh = Book.objects.get(title='Mostly Harmless')

手動選擇數據庫

Django 也提供壹個可以讓妳通過代碼完全控制數據庫使用的 API 。手動定義數據庫分配 優先於路由。

為壹個 查詢集 手動選擇壹個數據庫

妳可以在 查詢集 “鏈”中的任何點為 查詢集 選擇數據庫。我們通過在 查詢集 上調用 using() 來得到使用指定數據庫的另壹個 查詢集 。

using() 使用壹個參數:妳想要運行查詢的數據庫的別名。例如:

>>> # 這會運行在“缺省”數據庫上。 >>> Author.objects.all() >>> # 這同樣會運行在“缺省”數據庫上。 >>> Author.objects.using('default').all() >>> # 這會運行在“ other ”數據庫上。 >>> Author.objects.using('other').all()

為 save() 選擇壹個數據庫

在使用 Model.save() 時加上 using 關鍵字可以指定保存到哪個數據庫。

例如,要把壹個對象保存到 legacy_users 數據庫應該這樣做:

>>> my_object.save(using='legacy_users')

如果妳不定義 using ,那麽 save() 方法會根據路由分配把數據保存到缺省 數據庫中。

把壹個對象從壹個數據庫移動到另壹個數據庫

當妳已經在壹個數據庫中保存了壹個對象後,妳可能會使用 save(using=...) 把這個 對象移動到另壹個數據庫中。但是,如果妳沒有使用恰當的方法,那麽可能會出現意想不 到的後果。

假設有如下的例子:

>>> p = Person(name='Fred') >>> p.save(using='first') # (第壹句) >>> p.save(using='second') # (第二名)

在第壹名中,壹個新的 Person 對象被保存到 first 數據庫中。這時, p 還沒有壹個主鍵,因此 Django 執行了壹個INSERT SQL 語句。這樣就會創建壹個 主鍵,並將這個主鍵分配給 p 。

在第二句中,因為 p 已經有了壹個主鍵,所以 Django 在保存對象時會嘗試在新的 數據庫中使用這個主鍵。如果 second數據庫中沒有使用這個主鍵,那就不會有問題, 該對象會復制到新數據庫。

然而,如果 p 的主鍵在 second 數據庫中已經使用過了,那麽 second 使用 這個主鍵的已存在的對象將會被 p 覆蓋。

有兩種方法可以避免上述情況的發生。第壹,妳可以清除實例的主鍵。如果壹個對象沒有 主主鍵,那麽 Django 會把它看作壹個新對象,在保存到 second 數據庫中時就不會 帶來數據的損失:

>>> p = Person(name='Fred') >>> p.save(using='first') >>> p.pk = None # 清除主鍵。 >>> p.save(using='second') # 寫入壹個全新的對象。

第二種方法是在 save() 方法中使用 force_insert 選項來保證 Django 執行 壹個 INSERT SQL:

>>> p = Person(name='Fred') >>> p.save(using='first') >>> p.save(using='second', force_insert=True)

這樣可以保證名為 Fred 的人員在兩個數據庫中使用相同的主鍵。如果在保存到 second 數據庫時主鍵已被占用,會拋出壹個錯誤。

選擇壹個要刪除數據的數據庫

缺省情況下,壹個現存對象從哪個數據庫得到,刪除這個對象也會在這個數據庫中進行:

>>> u = User.objects.using('legacy_users').get(username='fred') >>> u.delete() # 會從 `legacy_users` 數據庫中刪除

通過向 Model.delete() 方法傳遞 using 關鍵字參數可以定義在哪個數據庫中刪除 數據。 using 的用法與 save() 方法中使用這個參數類似。

例如,假設我們要把壹個用戶從 legacy_users 數據庫移動到 new_users 數據庫 可以使用如下命令:

>>> user_obj.save(using='new_users') >>> user_obj.delete(using='legacy_users')

多數據庫情況下使用管理器

在管理器上使用 db_manager() ,可以讓管理器訪問壹個非缺省數據庫。

例如,假設妳有壹個操作數據庫的自定義管理器 User.objects.create_user() 。

因為 create_user() 是壹個管理器方法,不是壹個 查詢集 ,所以妳不能

用 User.objects.using('new_users').create_user() 。( create_user() 方法

只能用於 User.objects 管理器,而不能用於,管理器衍生出的 查詢集 。) 解決方法是使用 db_manager() ,就象下面這樣:

User.objects.db_manager('new_users').create_user(...)

db_manager() 返回的是綁定到妳指定的數據庫的管理器的壹個副本。

多數據庫情況下使用 get_query_set()

如果妳在管理器中重載了 get_query_set() ,請確保在其父類中也調用了相同的方法 (使用 super() )或者正確處理管理器中的 _db 屬性(壹個包含要使用的數據庫 名稱的字符串)。

例如,如果妳要從 get_query_set 方法返回壹個自定義 查詢集 類,那麽妳可以 這樣做:

class MyManager(models.Manager): def get_query_set(self): qs = CustomQuerySet(self.model) if self._db is not None: qs = qs.using(self._db) return qs

在 Django 管理接口中使用多數據庫

Django 的管理接口沒有明顯支持多數據庫。如果想要支持的話妳必須寫自定義 ModelAdmin 。

如果要支持多數據庫,那麽 ModelAdmin 對象有五個方法要自定義:

class MultiDBModelAdmin(admin.ModelAdmin): # 為方便起見定義壹個數據庫名稱常量。 using = 'other' def save_model(self, request, obj, form, change): # 讓 Django 保存對象到 'other' 數據庫。 obj.save(using=self.using) def delete_model(self, request, obj): # 讓 Django 從 'other' 數據庫中刪除對象。 obj.delete(using=self.using) def queryset(self, request): # 讓 Django 在 'other' 數據庫中搜索對象。 return super(MultiDBModelAdmin, self).queryset(request).using(self.using) def formfield_for_foreignkey(self, db_field, request=None, **kwargs): # 讓 Django 基於 'other' 數據庫生成外鍵控件。 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): # 讓 Django 基於 'other' 數據庫生成多對多關系控件。 return super(MultiDBModelAdmin, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)