数据库自动路由

使用多数据库最简单的方法是建立一个数据库路由模式。默认的路由模式确保对象’粘滞‘在它们原始的数据库上(例如,从foo数据库中获取的对象将保存在同一个数据库中)。默认的路由模式还确保如果没有指明数据库,所有的查询都回归到default数据库中。

你不需要做任何事情来激活默认的路由模式 —— 它在每个Django项目上’直接‘提供。然而,如果你想实现更有趣的数据库分配行为,你可以定义并安装你自己的数据库路由。

数据库路由

数据库路由是一个类,它提供4个方法:

db_for_read(model,**hints)

建议model类型的对象的读操作应该使用的数据库。

如果一个数据库操作能够提供其它额外的信息可以帮助选择一个数据库,它将在hints字典中提供。合法的hints 的详细信息在下文给出。

如果没有建议,则返回None

db_for_write(model,**hints)

建议Model 类型的对象的写操作应该使用的数据库。

如果一个数据库操作能够提供其它额外的信息可以帮助选择一个数据库,它将在hints字典中提供。合法的hints 的详细信息在下文给出。

如果没有建议,则返回None

allow_relation(obj1,obj2,**hints)

如果obj1obj2之间应该允许关联则返回True,如果应该防止关联则返回False,如果路由无法判断则返回None。这是纯粹的验证操作,外键和多对多操作使用它来决定两个对象之间是否应该允许一个关联。

allow_migrate(db,app_label,model_name=None,**hints)

定义迁移操作是否允许在别名为db的数据库上运行。如果操作应该运行则返回True,如果不应该运行则返回False,如果路由无法判断则返回None

位置参数app_label是正在迁移的应用的标签。

大部分迁移操作设置model_name的值为正在迁移的模型的model._meta.model_name(模型的__name__的小写)。对于RunPythonRunSQL操作它的值为None,除非这两个操作使用hint 提供它。

hints用于某些操作来传递额外的信息给路由。

当设置了model_name时,hints通常通过键'model'包含该模型的类。注意,它可能是一个历史模型,因此不会有自定的属性、方法或管理器。你应该只依赖_meta

这个方法还可以用来决定一个给定数据库上某个模型的可用性。

注意,如果这个方法返回False,迁移将默默地不会在模型上做任何操作。这可能导致你应用某些操作之后出现损坏的外键、表多余或者缺失。

Changed in Django 1.8:

allow_migrate的签名与以前的版本相比发生了显着更改。有关详细信息,请参阅deprecation notes。

路由不必提供所有这些方法 —— 它可以省略一个或多个。如果某个方法缺失,在做相应的检查时Django 将忽略该路由。

例子:

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

现在我们将需要处理路由。首先,我们需要一个路由,它知道发送auth应用的查询到auth_db

classAuthRouter(object):"""Aroutertocontrolalldatabaseoperationsonmodelsintheauthapplication."""defdb_for_read(self,model,**hints):"""Attemptstoreadauthmodelsgotoauth_db."""ifmodel._meta.app_label=='auth':return'auth_db'returnNonedefdb_for_write(self,model,**hints):"""Attemptstowriteauthmodelsgotoauth_db."""ifmodel._meta.app_label=='auth':return'auth_db'returnNonedefallow_relation(self,obj1,obj2,**hints):"""Allowrelationsifamodelintheauthappisinvolved."""ifobj1._meta.app_label=='auth'or\obj2._meta.app_label=='auth':returnTruereturnNonedefallow_migrate(self,db,app_label,model=None,**hints):"""Makesuretheauthapponlyappearsinthe'auth_db'database."""ifapp_label=='auth':returndb=='auth_db'returnNone

我们还需要一个路由,它发送所有其它应用的查询到primary/replica 配置,并随机选择一个replica 来读取:

importrandomclassPrimaryReplicaRouter(object):defdb_for_read(self,model,**hints):"""Readsgotoarandomly-chosenreplica."""returnrandom.choice(['replica1','replica2'])defdb_for_write(self,model,**hints):"""Writesalwaysgotoprimary."""return'primary'defallow_relation(self,obj1,obj2,**hints):"""Relationsbetweenobjectsareallowedifbothobjectsareintheprimary/replicapool."""db_list=('primary','replica1','replica2')ifobj1._state.dbindb_listandobj2._state.dbindb_list:returnTruereturnNonedefallow_migrate(self,db,app_label,model=None,**hints):"""Allnon-authmodelsendupinthispool."""returnTrue

最后,在设置文件中,我们添加如下内容(替换path.to.为该路由定义所在的真正路径):

DATABASE_ROUTERS=['path.to.AuthRouter','path.to.PrimaryReplicaRouter']