Django 1.2中的新功能:請參見發行說明
大多數其他文檔假設使用單個數據庫。本文主要討論如何在Django中使用多個數據庫。使用多個數據庫需要增加壹些步驟。
定義您的數據庫
使用多個數據庫的第壹步是設置要通過數據庫使用的數據庫服務。該設置用於映射數據庫別名和特定的連接設置字典,這是Django定義數據庫的壹貫方式。有關字典的內部設置,請參見數據庫文檔。
數據庫可以使用任何別名,但是default具有特殊的意義。當沒有選擇其他數據庫時,Django總是使用別名為default的數據庫。因此,如果您沒有定義壹個名為default的數據庫,您應該小心,在使用它之前指定您想要使用的數據庫。
下面是定義兩個數據庫的settings.py的代碼片段。定義壹個默認的PostgreSQL數據庫和壹個名為users:
DATABASES = { ' default ':{ ' NAME ':' app _ data ',' ENGINE ':' django . db . backends . PostgreSQL _ psycopg 2 ',' USER': 'postgres_user ',' PASSWORD': 's3krit' },' users': { 'NAME': 'user_data ',' ENGINE ':' django . db . backends . MySQL ',' USER': 'mysql_user ',' PASSWORD': 'priv4te' } }
如果妳試圖訪問壹個沒有在數據庫設置中定義的數據庫,django將拋出壹個Django。db . utils . connectiondonoenotexist異常。
同步您的數據庫
Syncdb管理命令壹次只能對壹個數據庫進行操作。默認情況下,它運行默認數據庫。但是使用- database參數,您可以讓syncdb同步不同的數據庫。因此,在我們的示例中,要同步所有數據庫的所有模型,可以使用以下命令:
$ ./manage.py syncdb
$ ./manage . py sync db-database = users
如果您沒有將所有程序同步到同壹個數據庫,您可以定義壹個數據庫路由來實現指定模型的特定控制策略。
如果您想精細地控制同步,還有壹種方法可以修改sqlall的輸出,並在數據庫中手動執行命令。這些命令如下所示:
$ ./manage.py sqlall sales |。/manage.py dbshell
使用其他管理命令
其他操作數據庫的django-admin.py命令類似於syncdb。他們壹次只操作壹個數據庫,並使用- database來控制使用哪個數據庫。
自動數據庫路由
使用多個數據庫最簡單的方法是建立壹個數據庫路由方案。默認的路由方案確保對象“接近”其原始數據庫(例如,對象被保存回其來源數據庫)。默認路由方案還確保了如果沒有指定數據庫,所有查詢都將作用於默認數據庫。
您不必做任何事情來啟動默認路由方案,因為它是“現成的”。但是,如果您想執行壹些更有趣的數據庫分配行為,您可以定義並安裝自己的數據庫路由。
數據庫路由
數據庫路由是壹個類,最多有四種方法:
db_for_read(型號,* *提示)
建議使用數據庫來編寫模型對象。
如果數據庫操作可以提供對選擇數據庫有用的附加信息,則可以通過提示字典來提供。詳見下文。
如果沒有建議,則返回None。
db_for_write(型號,* *提示)
建議使用數據庫來讀取模型對象。
如果數據庫操作可以提供對選擇數據庫有用的附加信息,則可以通過提示字典來提供。詳見下文。
如果沒有建議,則返回None。
allow_relation(obj1,obj2,* *提示)
當obj1和obj2的關系允許時返回True,不允許時返回False,沒有意見時返回None。這是壹個純驗證操作,用在外鍵和多對多操作中,驗證兩個對象之間的關系是否允許。
allow_syncdb(數據庫,型號)
確定模型是否可以與以db作為別名的數據庫同步。如果能返回True,如果不能返回False,或者沒有意見,則不返回。此方法用於確定給定數據庫的模型是否可用。
路由不必提供所有這些方法,可以省略其中的壹個或多個。如果省略其中壹個方法,Django在執行相關檢查時會跳過相應的路線。
提示參數
數據庫路由接收的“提示”參數可用於決定哪個數據庫應該接收給定的請求。
目前唯壹能提供的提示參數是實例,即與讀寫操作相關的對象的實例。它可以是已保存對象的實例,也可以是多對多關系中添加的實例。在某些情況下,可能沒有要提供的對象實例。路由將檢查提示實例是否存在,並決定是否相應地更改路由行為。
使用路由
使用DATABASE_ROUTERS設置安裝數據庫路由。這個設置定義了壹個類名列表,每個類定義壹個主路由(django.db.router)的路由。
主路由用於Django分配數據庫操作。當壹個查詢想要知道使用哪個數據庫時,它會提供壹個模型和壹個提示(如果有的話)並調用主路由。
姜戈會按順序嘗試每條路線,
直到找到合適的路由建議。如果沒有找到路由建議,將嘗試實例提示符的current _state.db。如果沒有提供路由提示,或者實例沒有當前數據庫狀態,則
主路由分配壹個默認數據庫。
壹個例子
僅用於示例目的!
此示例僅用於展示路由如何改變數據庫的使用。這個例子故意忽略了壹些復雜的東西,以便更好地展示路由是如何工作的。
如果myapp中的任何模型包含與另壹個數據庫中的模型的關系,則此示例無效。請參閱跨數據庫關系壹節中介紹的Django引用完整性問題。
這個例子的主/從配置也是有缺陷的:它沒有處理復制延遲(例如,將寫操作耗時地轉移到從數據庫導致的查詢不壹致),也沒有考慮與數據庫使用策略的交互。
那麽,這個例子有什麽用呢?此示例僅用於演示myapp存在於其他數據庫中,並且所有其他模型具有主/從關系,並且存在於master、slave1和slave2數據庫中。該示例使用兩條路線:
類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模型。_ meta。app _ label = = ' myapp ':return ' other ' return none def allow _ relation(self,obj1,obj2,**hints):“如果包含myapp應用中的模型,則允許所有關系”如果obj1。_ meta。app _ label =' myapp '或obj2。_ meta。app _ label = = ' myapp ':return true return none def allow _ syncdb(se lf,db,model):“確保myapp應用程序只存在於' other '數據庫中”if db = =' other': return model。_ meta。app _ label = =' myapp' elif模型。_ meta。app _ label = =' myapp ':返回false return。None類masterslaverouter (object):“具有簡單主/從定義的路由”“def db_for_read(self,model,* *提示):“所有讀取操作都指向隨機從數據庫”返回random。Choice (['slave1 ',' slave2']) def db _ for _ write (self,model,* *提示):“所有寫操作都指向主數據庫”return ' master ' def Allow _ relation(self,obj1,obj2,* *提示):“允許數據庫池中兩個對象之間的任何關系”db_list = ('master ',slave1 ',' slave 2') if obj 1。_ state。db _ list和obj 2中的db。_ state。db _ list中的db:Return True Return none def allow _ syncdb(self,db,model):“顯示所有數據庫中的模型”返回True。
然後將以下內容添加到設置文件中(將path.to替換為定義路線的模型的路徑):
DATABASE _ ROUTERS =[' path . to . my approuter ',' path.to.MasterSlaveRouter']
在此設置中,路由的順序非常重要,因為查詢是按照此設置中的順序進行的。在上面的例子中,MyAppRouter在MasterSlaveRouter之前,所以myapp中的模型優先於其他模型。如果更改了DATABASE_ROUTERS設置中兩條路由的順序,MasterSlaveRouter.allow_syncdb()將優先。因為MasterSlaveRouter是全包的,這就導致所有型號都可以使用所有數據庫。
設置完成後,讓我們運行壹些代碼:
& gt& gt& gt#從“憑據”數據庫獲取數據> & gt& gtfred = User.objects.get(用戶名= ' Fred ')& gt;& gt& gt弗雷德. first_name = '弗雷德裏克' & gt& gt& gt#保存到‘憑證’數據庫> & gt& gtFred . save()& gt;& gt& gt#從數據庫中隨機獲取數據> & gt& gtdna = Person.objects.get(name= '道格拉斯·亞當斯')& gt& gt& gt#創建新對象時未分配數據庫> & gt& gtmh = Book(title= '大部分無害')& gt& gt& gt#此賦值將向路由發送請求,並將mh的數據庫設置為與author對象相同>;& gt& gt# Database > & gt& gtmh.author = dna & gt& gt& gt#這將強制“mh”實例使用主數據庫...> & gt& gtMH . save()& gt;& gt& gt# ...但是如果我們再次獲取對象,我們將從數據庫>中獲取;& gt& gtmh = Book.objects.get(title= '大部分無害')
手動選擇數據庫
Django還提供了壹個API,允許妳通過代碼完全控制數據庫的使用。手動定義數據庫分配優先於路由。
為查詢集手動選擇數據庫。
您可以在查詢集“鏈”中的任何壹點為查詢集選擇壹個數據庫。我們通過對查詢集調用using()來獲得使用指定數據庫的另壹個查詢集。
使用()需要壹個參數:要運行查詢的數據庫的別名。例如:
& gt& gt& gt#這將在“默認”數據庫上運行。& gt& gt& gtauthor . objects . all()& gt;& gt& gt#這也將在“默認”數據庫上運行。& gt& gt& gtAuthor.objects.using('default ')。all()& gt;& gt& gt#這將在“其他”數據庫上運行。& gt& gt& gtAuthor.objects.using('other ')。全部()
選擇要保存的數據庫()
使用Model.save()時添加using關鍵字可以指定保存到哪個數據庫。
例如,要將對象保存到legacy_users數據庫,您應該執行以下操作:
& gt& gt& gtmy_object.save(使用='legacy_users ')
如果沒有定義using,save()方法會根據路由分配將數據保存到默認數據庫中。
將對象從壹個數據庫移動到另壹個數據庫。
當您在壹個數據庫中保存壹個對象時,您可以使用save(使用=...)將對象移動到另壹個數據庫。但是,如果妳沒有使用適當的方法,可能會有意想不到的後果。
假設有以下例子:
& gt& gt& gtp = Person(name = ' Fred ')& gt;& gt& gtP.save(using='first') #(第壹句)> & gt& gtP.save(using='second') #(第二名)
首先,在第壹個數據庫中保存壹個新的Person對象。此時,P沒有主鍵,所以Django執行了壹個INSERT SQL語句。這將創建壹個主鍵並將其分配給p。
在第二句話中,因為P已經有了壹個主鍵,所以Django在保存對象時會嘗試在新數據庫中使用這個主鍵。如果第二個數據庫中沒有使用這個主鍵,就不會有問題,對象將被復制到新的數據庫中。
但是,如果p的主鍵已經在第二個數據庫中使用,那麽在第二個數據庫中使用這個主鍵的現有對象將被p覆蓋。
有兩種方法可以避免上述情況。首先,您可以清除實例的主鍵。如果壹個對象沒有主鍵,Django會把它當作壹個新對象,保存在第二個數據庫中也不會帶來數據丟失:
& gt& gt& gtp = Person(name = ' Fred ')& gt;& gt& gtp . save(using = ' first ')& gt;& gt& gtP.pk = None #清除主鍵。& gt& gt& gtP.save(using='second') #編寫壹個全新的對象。
第二種方法是在save()方法中使用force_insert選項來確保Django執行INSERT SQL:
& gt& gt& gtp = Person(name = ' Fred ')& gt;& gt& gtp . save(using = ' first ')& gt;& gt& gtp.save(使用='second ',force_insert=True)
這確保了名為Fred的人在兩個數據庫中使用相同的主鍵。如果保存到第二個數據庫時主鍵已被占用,將會引發錯誤。
選擇要從中刪除數據的數據庫。
默認情況下,壹個已有的對象是從哪個數據庫中獲取的,刪除這個對象也將在這個數據庫中進行:
& gt& gt& gtu = user . objects . using(' legacy _ user ')。get(username = ' Fred ')& gt;& gt& gtU.delete() #將從legacy_users數據庫中刪除。
通過將using關鍵字參數傳遞給Model.delete()方法,可以定義要在哪個數據庫中刪除數據。使用類似於在save()方法中使用此參數。
例如,假設我們想要將用戶從legacy_users數據庫移動到new_users數據庫,我們可以使用以下命令:
& gt& gt& gtuser _ obj . save(using = ' new _ users ')& gt;& gt& gtuser_obj.delete(使用='legacy_users ')
在多數據庫情況下使用管理器
在管理器上使用db_manager()允許管理器訪問非默認數據庫。
例如,假設您有壹個操作數據庫的自定義管理器User.objects.create_user()。
因為create_user()是壹個管理器方法,而不是壹個查詢集,所以您不能
use user . objects . using(' new _ users ')。create _ user()。(create_user()方法。
只能在User.objects管理器中使用,不能在從管理器派生的查詢集中使用。)解決方案是使用db_manager(),如下所示:
user . objects . db _ manager(' new _ users ')。創建用戶(...)
Db_manager()返回綁定到指定數據庫的管理器的副本。
在多個數據庫的情況下使用get_query_set()。
如果在管理器中重載get_query_set(),請確保在其父類中調用相同的方法(使用super())或者正確處理管理器中的_db屬性(包含要使用的數據庫名稱的字符串)。
例如,如果要從get_query_set方法返回自定義查詢集類,可以這樣做:
MyManager類(型號。manager):def get _ query _ set(self):QS = CustomQuerySet(self . model)if self。_db不是None: qs = qs.using(self。_db)返回qs
在Django管理界面中使用多數據庫
Django的管理界面顯然不支持多數據庫。如果妳想支持它,妳必須寫壹個自定義的ModelAdmin。
如果您想要支持多個數據庫,ModelAdmin對象有五種方法可以自定義:
類multidbmodeadmin (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查詢集(self,request): #讓Django在' other '數據庫中搜索對象。返回super(multimodeladmin,self)。查詢集(請求)。利用(自我。using)def form field _ for _ foreign key(self,db _ field,request = none,* * kwargs): #讓Django基於' other '數據庫生成外鍵控件。返回super(MultiDBModelAdmin,self)。form field _ for _ foreign key(db_field,request=request,using=self.using,* * kwargs)defformfield _ for _ manytomany(self,db _ field,request = none,* * kwargs): #讓Django基於' other '數據庫生成多對多關系控件。返回super(MultiDBModelAdmin,self)。form field _ for _ manytomany(db _ field,request=request,using=self.using,**kwargs)