Django多数据库配置与路由实现详解
本文详解Django项目中配置和使用多个数据库的方法。涵盖在settings中定义DATABASES、通过app_label指定模型归属,以及编写Database Routers类实现读写分离和迁移控制。包含AuthRouter与PrimaryReplicaRouter完整示例,讲解migrate命令的--database参数使用,以及手动指定using、处理主键冲突和对象转移的具体代码实现。
Django多数据库配置概述
部分项目场景下需要同时操作多个数据库,实现这一目标的方法相对直接。
Settings中配置DATABASES
第一步是在项目的settings配置文件中定义DATABASES字典。
假设项目需要同时连接两个不同的数据库实例。
DATABASES = {
'default': {
'NAME': 'app_data',
'ENGINE': 'django.db.backends.postgresql',
'USER': 'postgres_user',
'PASSWORD': 's3krit'
},
'users': {
'NAME': 'user_data',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'priv4te'
}
}
通过上述配置,系统便识别出两个数据库连接,分别命名为default和user。这里的数据库别名可以根据实际需求自由命名。
其中,default别名具有特殊意义:如果某个Model在路由配置中未指定特定数据库,系统将默认使用名为default的数据库进行读写操作。
此外,也可以将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'
}
}
一旦default被设为空,意味着系统不再拥有默认数据库。此时,必须为所有Model(包括第三方库中引入的Model)明确指定数据库路由策略。
第二步,为需要指定数据库归属的Model设置app_label属性。
class MyUser(models.Model):
...
class Meta:
app_label = 'users'
Database Routers路由规则编写
第三步,编写Database Routers类来控制数据库选择逻辑。
Database Router的核心作用是决定每个Model使用哪个数据库,主要需实现以下四个方法:
db_for_read(model, **hints)
该方法用于指定model执行读取操作时使用的数据库。
db_for_write(model, **hints)
该方法用于指定model执行写入操作时使用的数据库。
allow_relation(obj1, obj2, **hints)
该方法用于判断obj1与obj2之间是否允许建立关联,主要应用于ForeignKey和ManyToManyField关系操作。
allow_migrate(db, app_label, model_name=None, **hints)
该方法用于确定名为db的数据库是否允许执行migrate迁移操作。
完整路由配置示例
以下是一个完整的配置示例。
数据库连接配置如下:
DATABASES = {
'default': {},
'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',
},
}
若希望实现如下路由效果:
当app_label为auth的Model进行读写操作时,均指向auth_db数据库;其余Model的写入操作指向primary数据库,而读取操作则随机分配至replica1或replica2数据库。
针对auth应用的配置:
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_name=None, **hints):
"""
Make sure the auth app only appears in the 'auth_db'
database.
"""
if app_label == 'auth':
return db == 'auth_db'
return None
通过上述配置,app_label为auth的Model的所有读写操作均在auth_db中完成,允许相关对象建立关联,且migrate迁移操作仅在auth_db数据库中执行。
针对其他应用的配置:
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_name=None, **hints):
"""
All non-auth models end up in this pool.
"""
return True
通过上述配置,读取操作将随机在replica1和replica2之间选择,而写入操作统一使用primary数据库。
最后,在settings.py文件中配置DATABASE_ROUTERS列表。
DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.PrimaryReplicaRouter']
完成上述配置后,多数据库路由即可生效。
数据库迁移与管理命令
执行migrate迁移命令时:
$ ./manage.py migrate
$ ./manage.py migrate --database=users
migrate命令默认针对default数据库执行。若需对其他数据库执行迁移,可添加--database选项,后接目标数据库的别名。
同样地,dbshell、dumpdata和loaddata等管理命令也支持--database选项。
手动指定数据库与对象转移
开发者也可以手动指定数据库路由。
例如执行查询操作:
>>> # 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()
例如执行保存操作:
>>> my_object.save(using='legacy_users')
例如执行移动或转移对象操作:
>>> p = Person(name='Fred')
>>> p.save(using='first') # (statement 1)
>>> p.save(using='second') # (statement 2)
上述代码逻辑可能存在潜在问题:当对象p在first数据库中首次保存时,系统会自动生成主键。若随后尝试将p保存至second数据库,由于p已拥有主键,若该主键未被其他记录使用则无影响;但若该主键已被占用,则可能导致数据覆盖。
针对此问题,有两种解决方案:
方案一:在保存前清除主键。
>>> 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参数强制插入。
>>> p = Person(name='Fred')
>>> p.save(using='first')
>>> p.save(using='second', force_insert=True)
关于删除操作:
原则是从哪个数据库获取的对象,就从该数据库执行删除操作。
>>> u = User.objects.using('legacy_users').get(username='fred')
>>> u.delete() # will delete from the `legacy_users` database
若需将对象从legacy_users数据库迁移至new_users数据库:
>>> user_obj.save(using='new_users')
>>> user_obj.delete(using='legacy_users')
以上内容涵盖了Django多数据库配置的核心要点,希望对读者有所帮助,欢迎继续支持本站获取更多技术资讯。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













