Django——连接多个数据库的实现方式
Django——连接多个数据库的实现方式
最近刚刚忙完手头的工作,所以决定把这一个项目里面的坑陆续梳理出来,一方面以后自己回顾的时候有个参考算是备忘,另一方面希望能帮到遇到跟我一样问题的兄弟。
*首先声明,我所用的版本是python3.5和Django2.0.4,公司使用的是Django,没有用restframework。
一、应用场景
首先一个Django项目里通常会有多个app,现在很多公司在这多个app中都是共用的一个数据库。在这种场景中是不存在所谓连多个数据库的。那以我的个人经历来说,公司近两个月让我陆陆续续做了一些小的系统(都是用于公司内部数据分拣的),因为每个都很急用,所以就做完一个上线一个,数据库和项目都部在不同服务器上了。直到有一天我们老总把我叫过去:“小肖啊,你抽时间把你做的几个系统合一下,咱们把它合成一个系统。就是登陆以后,出一个系统选择页面,选哪个就进那个系统。”
之后我一番奋战,把代码什么的合并到一起了。但是,发现数据不在一个库里,王炸!那么就有两种选择:
1.把所有的模型都通过默认的数据库放在一起,那么这样做就需要把之前处理的数据抽出来,并且对模型进行修改,把一些重复的表名进行一些区分;
2.是依然让数据存在之前的库里,把这些数据库迁移出来,抽取出Django自建的表,然后通过不同的数据库进行不同操作,这样也做到了不同系统间的数据的隔离。
所以最后我选用了第二种方案来做这个事情。
二、代码实现
代码中主要是三个部分,settings、models以及自己写的一个类。首先看看我们写的那个类:
1 from django.conf import settings 2 3 DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING 4 5 class DatabaseAppsRouter(object): 6 """ 7 A router to control all database operations on models for different 8 databases. 9 10 In case an app is not set in settings.DATABASE_APPS_MAPPING, the router 11 will fallback to the `default` database. 12 13 Settings example: 14 15 DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'} 16 """ 17 18 def db_for_read(self, model, **hints): 19 """"Point all read operations to the specific database.""" 20 """将所有读操作指向特定的数据库。""" 21 if model._meta.app_label in DATABASE_MAPPING: 22 return DATABASE_MAPPING[model._meta.app_label] 23 return None 24 25 def db_for_write(self, model, **hints): 26 """Point all write operations to the specific database.""" 27 """将所有写操作指向特定的数据库。""" 28 if model._meta.app_label in DATABASE_MAPPING: 29 return DATABASE_MAPPING[model._meta.app_label] 30 return None 31 32 def allow_relation(self, obj1, obj2, **hints): 33 """Allow any relation between apps that use the same database.""" 34 """允许使用相同数据库的应用程序之间的任何关系""" 35 db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label) 36 db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label) 37 if db_obj1 and db_obj2: 38 if db_obj1 == db_obj2: 39 return True 40 else: 41 return False 42 else: 43 return None 44 45 def allow_syncdb(self, db, model): 46 """Make sure that apps only appear in the related database.""" 47 """确保这些应用程序只出现在相关的数据库中。""" 48 if db in DATABASE_MAPPING.values(): 49 return DATABASE_MAPPING.get(model._meta.app_label) == db 50 elif model._meta.app_label in DATABASE_MAPPING: 51 return False 52 return None 53 54 def allow_migrate(self, db, app_label, model=None, **hints): 55 """Make sure the auth app only appears in the 'auth_db' database.""" 56 """确保身份验证应用程序只出现在“authdb”数据库中。""" 57 if db in DATABASE_MAPPING.values(): 58 return DATABASE_MAPPING.get(app_label) == db 59 elif app_label in DATABASE_MAPPING: 60 return False 61 return None
这个类主要是规范了数据库的一些读写操作。要注意,这个文件是放在和settings.py同级的目录下的。代码逻辑比较好理解,我就不解释了。用的时候直接复制粘贴一把梭,然后自己再做一些逻辑处理上的修改就可以了。
在settings中是配置三个地方,代码如下:
1 # Database 2 # https://docs.djangoproject.com/en/2.0/ref/settings/#databases 3 4 DATABASES = { 5 'default':{ 6 'NAME': 'venn', 7 'ENGINE': 'sql_server.pyodbc', 8 'HOST': '127.0.0.1', 9 'PORT': '1433', 10 'USER': 'venndata', 11 'PASSWORD': 'venndata', 12 'OPTIONS':{ 13 'driver':'SQL Server Native Client 10.0', 14 } 15 }, 16 'venn': { 17 'NAME': 'venn', 18 'ENGINE': 'sql_server.pyodbc', 19 'HOST': '127.0.0.1', 20 'PORT': '1433', 21 'USER': 'venndata', 22 'PASSWORD': 'venndata', 23 'OPTIONS':{ 24 'driver':'SQL Server Native Client 10.0', 25 } 26 }, 27 'vip_cluster': { 28 'NAME': 'vip_cluster', 29 'ENGINE': 'sql_server.pyodbc', 30 'HOST': '127.0.0.1', 31 'PORT': '1433', 32 'USER': 'venndata', 33 'PASSWORD': 'venndata', 34 'OPTIONS':{ 35 'driver':'SQL Server Native Client 10.0', 36 } 37 }, 38 'catecheck': { 39 'NAME': 'catecheck', 40 'ENGINE': 'sql_server.pyodbc', 41 'HOST': '127.0.0.1', 42 'PORT': '1433', 43 'USER': 'venndata', 44 'PASSWORD': 'venndata', 45 'OPTIONS':{ 46 'driver':'SQL Server Native Client 10.0', 47 } 48 }, 49 'skucheck': { 50 'NAME': 'skucheck', 51 'ENGINE': 'sql_server.pyodbc', 52 'HOST': '127.0.0.1', 53 'PORT': '1433', 54 'USER': 'venndata', 55 'PASSWORD': 'venndata', 56 'OPTIONS':{ 57 'driver':'SQL Server Native Client 10.0', 58 } 59 }, 60 'barcode': { 61 'NAME': 'barcode', 62 'ENGINE': 'sql_server.pyodbc', 63 'HOST': '127.0.0.1', 64 'PORT': '1433', 65 'USER': 'venndata', 66 'PASSWORD': 'venndata', 67 'OPTIONS':{ 68 'driver':'SQL Server Native Client 10.0', 69 } 70 } 71 } 72 73 74 DATABASE_ROUTERS = ['vennsystem.database_router.DatabaseAppsRouter'] 75 76 77 DATABASE_APPS_MAPPING = { 78 # example: 79 # 'app_name':'database_name', 80 'venncheck': 'skucheck', 81 'barcode': 'barcode', 82 'catecheck': 'catecheck', 83 'clust': 'vip_cluster', 84 'the_entrance': 'venn', 85 'admin': 'venn', 86 'auth': 'venn', 87 'contenttypes': 'venn', 88 'sessions': 'venn', 89 }
DATABASES大家都知道,是配置数据库连接的(这里要注意的是我用的是sql server数据库,里面OPTIONS是非常重要的参数,一定要把这个加进去,不然跑不起来)。default是默认的数据库,在这里可以为{},但是一旦为空不可以执行'python manage.py migrate'(会在下个部分说怎么处理)
之后是DATABASE_ROUTERS这个指向的是我们自己写的那个类(['项目名.文件名.类名'])。最后是路由分配,把app的名字和数据库对应起来。
在模型中的部分特别简单,只要加两行代码就可以解决:
1 class Href(models.Model): 2 name = models.CharField(max_length=100) 3 path = models.CharField(max_length=100) 4 5 class Meta: 6 app_label = 'the_entrance'
就是这样的东西,加上app_label以后就会指明所属的app。
三、执行
执行的顺序就是大家熟悉的了:
1.python manage.py makemigrations
之后的稍有不同:
2.python manage.py migrate --database=skucheck
python manage.py migrate --database=barcode
......
python manage.py migrate(只有默认数据库不为空时才可以这么实用,若为空则是用上面的方法)
其实在这里没有特别的执行顺序,但是我个人建议大家是最后执行migrate(即默认数据库)。另外要注意一点,admin、auth、contenttypes和sessions是一定要在一个app里被makemigrations放到一个XXXX_initial.py文件中的,不然你怎么migrate都不会有这些Django自建的表的!
****最后的最后一定要注意:已经不在一个库里了,没法跨库建立约束关系,放弃外键,老实的一步步查!****