Django 多數據庫聯用(同一個APP的models里不同class用不同數據庫)


很多網站有多數據庫聯用的文章,如自強學堂http://code.ziqiangxuetang.com/django/django-multi-database.html
大都只講解如何讓不同的app對應不同的數據庫,而沒有談到如何讓同一個app里的不同class對應不同的數據庫。
經過N多次試驗,歷經好幾天時間,終於找出如下的簡便易行的途徑,而不需要自己造輪子,現總結如下:

方式一:通過數據庫路由自動分發Model,無需手動using

settings配置如下:

1、增加了DATABASE_ROUTERS,用於指定路由路徑
2、增加了DATABASE_APPS_MAPPING,指定app_label對應的數據庫

DBNAME = 'multi_test' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, 'db1': { 'ENGINE': 'django.db.backends.mysql', 'NAME': DBNAME, 'USER': 'root', 'PASSWORD': 'xxx', 'HOST': '127.0.0.1', 'PORT': 3306, 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", }, }, 'db2': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': DBNAME, 'USER': 'root', 'PASSWORD': 'xxx', 'HOST': '127.0.0.1', 'PORT': 5432, }, 'db3': { 'ENGINE': 'django.db.backends.dummy', }, } connect(DBNAME) #connect to mongodb SUB_DIR = os.path.basename(os.path.dirname(__file__)) DATABASE_ROUTERS = [ '{}.database_router.DatabaseAppsRouter'.format(SUB_DIR) ] DATABASE_APPS_MAPPING = { # example: #'app_label':'database_name', 'mysql': 'db1', 'postgres': 'db2', } # 'mysql', 'postgres'需加到INSTALLED_APPS中,它們是通過startapp創建的兩個空app 

在settings同目錄下增加database_router.py 文件,內容如下:
(直接從自強學堂拷貝,只注釋掉print語句,無需做其他更改)

# -*- coding: utf-8 -*- from django.conf import settings DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING class DatabaseAppsRouter(object): """ A router to control all database operations on models for different databases. In case an app is not set in settings.DATABASE_APPS_MAPPING, the router will fallback to the `default` database. Settings example: DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'} """ 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 return None # for Django 1.4 - Django 1.6 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 # Django 1.7 - Django 1.11 def allow_migrate(self, db, app_label, model_name=None, **hints): """ Make sure that apps only appear in the related database. 根據app_label的值只在相應的數據庫中創建一個表,如果刪除該def或 不指定過濾條件,則一個Model會在每個數據庫里都創建一個表。 """ if db in DATABASE_MAPPING.values(): return DATABASE_MAPPING.get(app_label) == db elif app_label in DATABASE_MAPPING: return False return None 

models里在class Meta中指定app_label就可以指定它要使用的數據庫

例如:settings的DATABASE_APPS_MAPPING中指定app1對應db1、app2對應db2,
那么app1中的Model默認使用db1,而當指定Meta中的app_label為app2時,則改用db2。
注:app_label指定的值須包含在settings的INSTALLED_APPS中,否則makemigrations不會自動創建表。
app1/models.py內容如下:

#coding=utf-8 from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Animal(models.Model): name = models.CharField(max_length=20) def __str__(self): return self.name class Meta: # 通過app_label來指定要使用的數據庫 # 需指定db_table,否則該class的表名會是mysql_animal # 如果不指定Meta的app_label,會使用默認數據庫 app_label = 'mysql' db_table = 'app1_animal' @python_2_unicode_compatible class Plant(models.Model): name = models.CharField(max_length=20) def __str__(self): return self.name class Meta: app_label = 'postgres' db_table = 'app1_plant' 

app2/models.py

#coding=utf-8 from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Fruit(models.Model): name = models.CharField(max_length=20) def __str__(self): return self.name class Meta: app_label = 'mysql' #使用mysql數據庫 db_table = 'app2_fruit' #指定表名為app2_fruit @python_2_unicode_compatible class Nut(models.Model): name = models.CharField(max_length=20) def __str__(self): return self.name class Meta: app_label = 'postgres' #使用postgres數據庫 db_table = 'app2_nut' #指定表名為app2_fruit 

app1/test.py

from django.test import TestCase

from app1.models import Animal, Plant
from app2.models import Fruit, Nut

class ModelsTestCase(TestCase): # db1 -- mysql # db2 -- postgres def setUp(self): Animal.objects.create(name='db1') Plant.objects.create(name='db2') Fruit.objects.create(name='mysql_fruit') Nut.objects.create(name='post_nut') def test_methods_auto_choose_db(self): self.assertEqual(Animal.objects.all()[0], Animal.objects.using('db1').all()[0]) self.assertEqual(Plant.objects.all()[0], Plant.objects.using('db2').all()[0]) self.assertEqual(Fruit.objects.all()[0], Fruit.objects.using('db1').all()[0]) self.assertEqual(Nut.objects.all()[0], Nut.objects.using('db2').all()[0]) self.assertNotEqual(Animal.objects.all()[0], Fruit.objects.using('db1').all()[0]) self.assertNotEqual(Plant.objects.all()[0], Nut.objects.using('db2').all()[0]) self.assertNotEqual(Fruit.objects.all()[0], Plant.objects.using('db2').all()[0]) 

然后在命令行中運行

python manage.py makemigrations
python manage.py migrate --database=db1
python manage.py migrate --database=db2
python manage.py migrate
python manage.py test 

完整代碼放在coding上:

git clone https://git.coding.net/zhengwenjie/multi_dbs.git cd multi_dbs virtualenv venv source venv/bin/activate pip install -r requirements.txt vi multi_dbs/settings.py #修改數據庫名和密碼等 alias mg='python manage.py' mg makemigrations mg migrate --database=db1 mg migrate --database=db2 mg migrate mg test 

方式二:通過using手動指定數據庫

例如settings中的數據庫配置如下:

# Database # https://docs.djangoproject.com/en/1.11/ref/settings/#databases import pymysql pymysql.install_as_MySQLdb() from mongoengine import connect DBNAME = 'multi_test' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, 'mysql': { 'ENGINE': 'django.db.backends.mysql', 'NAME': DBNAME, 'USER': 'root', 'PASSWORD': 'xxx', 'HOST': '127.0.0.1', 'PORT': 3306, 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", }, }, 'postgres': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': DBNAME, 'USER': 'root', 'PASSWORD': 'xxx', 'HOST': '127.0.0.1', 'PORT': 5432, }, 'mongo': { 'ENGINE': 'django.db.backends.dummy', }, } 

則查詢和創建數據語句如下(不加using為采用default數據庫)

from app1.models import House House.objects.using('mysql').create(name='Tom') House.objects.using('mysql').get(name='Tom') House.objects.using('postgres').get_or_create(name='Jim')


作者:waketzheng
鏈接:https://www.jianshu.com/p/738645fc9230
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM