Django——多个数据库
官方文档:https://docs.djangoproject.com/en/3.2/topics/db/multi-db/
主要介绍Django 对与多个数据库交互的支持。
1. 数据库服务器 DATABASES
在Django中使用多个数据库的第一步是告诉Django将要使用的数据库服务器。 这是通过使用 DATABASES
设置完成的。 这个设置将数据库别名映射到特定连接的设置字典中,别名是在Django中引用特定数据库的一种方式。 内部字典中的设置在DATABASES文档中有完整的描述。
数据库可以有您选择的任何别名。 但是,别名默认值有特殊的意义。 当没有选择其他数据库时,Django会使用default
别名的数据库。
下面是 settings.py
代码片段的一个例子,它定义了两个数据库——一个默认的 PostgreSQL
数据库和一个名为users的 MySQL
数据库:
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'
}
}
如果默认数据库的概念在项目的上下文中没有意义,那么您需要始终谨慎地指定要使用的数据库。 Django要求定义一个默认的数据库条目,但是如果不使用的话,参数字典可以保留为空。 要做到这一点,你必须为所有应用程序的模型设置 DATABASE_ROUTERS
,包括你正在使用的任何应用程序和第三方应用程序的模型,这样查询就不会路由到默认数据库。
下面是 settings.py
代码段的一个示例,它定义了两个非默认数据库,默认条目故意为空:
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'
}
}
如果你试图访问一个没有在 database 设置中定义的数据库,Django会抛出 Django.utils.connection. connectiondoesnotexist
异常。
2. 同步数据库
迁移管理命令一次操作一个数据库。
(default是默认的数据库,在这里可以为{},但是一旦为空不可以执行 python manage.py migrate
)
默认情况下,它在默认数据库上操作,但是通过提供 ——database
选项,可以告诉它同步一个不同的数据库。 因此,要将上面第一个例子中的所有模型同步到所有数据库,
您需要调用:
python manage.py makemigrations # 生成迁移文件
python manage.py migrate --database=users
如果您不希望将每个应用程序同步到特定的数据库,那么可以定义一个数据库路由器,该路由器实现一个限制特定模型可用性的策略。
如果与上面的第二个示例一样,您将默认数据库保留为空,则必须在每次运行 migrate
时提供一个数据库名称(分多次迁移数据)。 省略数据库名称将引发错误。
对于第二个例子:
python manage.py migrate --database=users
python manage.py migrate --database=customers
3. 自动数据库路由(默认路由方案)
使用多个数据库的最简单方法是建立一个数据库路由方案。
默认的路由方案确保对象保持“粘性”到他们的原始数据库(即,从 xxx
数据库检索的对象将保存在同一个数据库)。 默认路由方案确保如果没有指定数据库,所有查询都返回到默认数据库。
你不需要做任何事情来激活默认路由方案——它是在每个Django项目中“开箱即用”提供的。
但是,如果您想实现更强大的数据库分配行为,您可以定义自己的数据库路由器。
4. 自定义路由方案(数据库路由器)
一个 database Router
是一个提供了四个方法的类:
db_for_read(model, **hints)
- 应用于读取类型对象的数据库模型,如果数据库提供附加信息会在hints字典中提供,最后如果没有则返回None
db_for_write(model, **hints)
- 应用于写入类型对象的数据库模型,hints字典提供附加信息,如果没有则返回None
allow_relation(obj1, obj2, **hints)
- 外键操作,判断两个对象之间是否是应该允许关系,是返回True,否则返回False,如果路由允许返回None
allow_migrate(db, app_label, model_name=None, **hints)
- db确定是否允许在具有别名的数据库上运行迁移操作,操作运行返回True,否则返回False,或者返回None,如果路由器没有意见。
- app_label:位置参数是正在迁移的应用程序的标签。
- model_name:多个迁移操作设置模型的值,如:model._meta.app_label
路由器不必提供所有这些方法——它可以省略一个或多个。 如果其中一个方法被省略,Django将在执行相关检查时跳过该路由器。
4.1 使用路由器
使用 DATABASE_ROUTERS
设置安装数据库路由器。
这个设置定义了一个类名列表,每个类名指定一个主路由器(django.db.router)使用的路由器。
主路由器用于Django的数据库操作来分配数据库的使用。 每当查询需要知道使用哪个数据库时,它就调用主路由器,提供一个模型和一个提示(如果可用的话)。 然后Django依次尝试每个路由器,直到找到数据库建议。 如果找不到提示,则尝试提示实例的当前 instance._state.db
。 如果没有提供提示实例,或者 instance._state.db
为None,主路由器将分配默认数据库。
4.2 案例
4.2.1 创建数据库路由
在 settings.py
同级目录下创建 数据库路由器( db_router.py
),app应用会根据指定的路由选择数据库:
from .settings import DATABASE_APPS_MAPPING
DATABASE_MAPPING = DATABASE_APPS_MAPPING
# 数据库路由分发
class database_router(object):
def db_for_read(self, model, **hints):
""""Point all read operations to the specific database."""
"""将所有读操作指向特定的数据库。"""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def db_for_write(self, model, **hints):
"""Point all write operations to the specific database."""
"""将所有写操作指向特定的数据库。"""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def allow_relation(self, obj1, obj2, **hints):
"""Allow any relation between apps that use the same database."""
"""允许使用相同数据库的应用程序之间的任何关系"""
db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
if db_obj1 and db_obj2:
if db_obj1 == db_obj2:
return True
else:
return False
else:
return None
def allow_syncdb(self, db, model):
"""Make sure that apps only appear in the related database."""
"""确保这些应用程序只出现在相关的数据库中。"""
if db in DATABASE_MAPPING.values():
return DATABASE_MAPPING.get(model._meta.app_label) == db
elif model._meta.app_label in DATABASE_MAPPING:
return False
return None
def allow_migrate(self, db, app_label, model=None, **hints):
"""Make sure the auth app only appears in the 'auth_db' database."""
"""确保身份验证应用程序只出现在“authdb”数据库中。"""
if db in DATABASE_MAPPING.values():
return DATABASE_MAPPING.get(app_label) == db
elif app_label in DATABASE_MAPPING:
return False
return None
4.2.2 settings.py
配置
在 setting.py
中配置 DATABASE_ROUTERS
指定自由路由文件:
# 数据库设置, xxxx0001为项目名称
DATABASE_ROUTERS = ['xxxx0001.db_router.database_router']
在 setting.py
中 DATABASE_ROUTERS
下面设置app与数据库匹配路由表,采用字典方式app名对应数据库映射名:
DATABASE_APPS_MAPPING = {
'app01':'default',
'app02':'db2',
}
4.2.3 生成表并同步数据
分别在app01和app02下创建model类,用于生成数据表:
此处只定义了 app02 的模型,注意:加上app_label以后就会指明所属的app
from django.db import models
class IntelligentTableView(models.Model):
table_name = models.CharField(max_length=255, verbose_name='报表名称')
center = models.CharField(max_length=50, verbose_name='所属中心')
on_line = models.CharField(max_length=50, verbose_name='所属线条')
remake = models.CharField(max_length=255, verbose_name='备注')
create_name = models.CharField(max_length=50, verbose_name='创建人')
create_date = models.DateTimeField(auto_now=True, verbose_name='创建时间')
is_del = models.BooleanField(default=False, verbose_name='是否删除')
class Meta:
db_table = 'intelligent_table' # 表名
verbose_name = '智能报表'
app_label = 'app02' # 指明app名称,用来对应app 和 数据库的map表
migrate管理命令一次只能操作一个数据库,默认操作default数据库,使用–database指定同步的数据库:
python manage.py migrate # 生成表数据同步
python manage.py makemigrations # # 生成迁移文件
python manage.py migrate --database=default # 同步指定数据库
python manage.py migrate --database=db2