mongoengine支持程序同時連接多個數據庫,這些數據庫可以位於一個或多個mongo之中,通過alias名稱區分不同的連接即可。
可以通過switch_db切換到不同的數據庫,進行讀寫操作,switch_db其實是一個上下文管理器,通過和with語句一起使用,能夠保證切換數據庫的影響范圍。
由於個人電腦上沒有安裝mongodb,以下代碼示例中訪問的mongodb通過mongoengine提供的mock mongodb替代,只需要在connect函數參數中加上is_mock=True參數,並且安裝有mongomock package(pip install mongomock)即可。
1 #!/usr/bin/env python 2 # coding=utf-8 3 from mongoengine import connect 4 from mongoengine.document import Document 5 from mongoengine.fields import StringField, IntField 6 from mongoengine.context_managers import switch_db 7 8 9 # 連接不同db 10 connect(alias='testdbA', is_mock=True) 11 connect(alias='testdbB', is_mock=True) 12 connect(alias='testdbC', is_mock=True) 13 14 15 class User(Document): 16 meta = { 17 # db_alias用於指定User綁定的mongo連接,和connect函數中的alias對應 18 'db_alias': 'testdbA', 19 } 20 uname = StringField(max_length=63) 21 uid = StringField(max_length=64) 22 age = IntField(default=0) 23 24 25 def create_record(uname, uid, age): 26 user = User() 27 user.uname = uname 28 user.uid = uid 29 user.age = age 30 return user 31 32 def print_records(records): 33 # 輸出User類此時綁定的db連接名稱,並輸出其記錄個數 34 records = list(User.objects.all()) 35 print('{} count:{}, records:'.format(User._meta['db_alias'], len(records))) 36 for rc in records: 37 print(' {}|{}|{}'.format(rc['uname'], rc['uid'], rc['age'])) 38 39 40 records = list(User.objects.all()) 41 print_records(records) 42 create_record('Ace', 'AX001', 10).save() 43 print_records(records) 44 45 # switch到testdbB連接 46 with switch_db(User, 'testdbB'): 47 print_records(records) 48 create_record('Bob', 'BX001', 11).save() 49 print_records(records) 50 51 # switch到testdbC連接 52 with switch_db(User, 'testdbC'): 53 print_records(records) 54 create_record('Carl', 'CX001', 12).save() 55 print_records(records)
運行結果如下:
$ python mongo_test.py testdbA count:0, records: testdbA count:1, records: Ace|AX001|10 testdbB count:0, records: testdbB count:1, records: Bob|BX001|11 testdbC count:0, records: testdbC count:1, records: Carl|CX001|12
switch_db其實就是一個簡單的上下文管理器,源碼位於mongoengine/context_managers.py當中,其實就是在switch_db對象初始化時、進入with代碼塊前、離開with代碼塊后,通過改變db_alias和collection兩個字段對應的取值,實現了不同mongodb連接的切換,代碼簡單注釋版本如下:
1 class switch_db(object): 2 """switch_db alias context manager. 3 4 Example :: 5 6 # Register connections 7 register_connection('default', 'mongoenginetest') 8 register_connection('testdb-1', 'mongoenginetest2') 9 10 class Group(Document): 11 name = StringField() 12 13 Group(name='test').save() # Saves in the default db 14 15 with switch_db(Group, 'testdb-1') as Group: 16 Group(name='hello testdb!').save() # Saves in testdb-1 17 """ 18 19 def __init__(self, cls, db_alias): 20 """Construct the switch_db context manager 21 22 :param cls: the class to change the registered db 23 :param db_alias: the name of the specific database to use 24 """ 25 self.cls = cls 26 # 存儲切換前的collection 27 self.collection = cls._get_collection() 28 # 設置為要切換的db連接名稱 29 self.db_alias = db_alias 30 # 存儲切換前的原db連接名稱 31 self.ori_db_alias = cls._meta.get('db_alias', DEFAULT_CONNECTION_NAME) 32 def __enter__(self): 33 """Change the db_alias and clear the cached collection.""" 34 # 進入with代碼塊前觸發 35 # 修改為本次指定的db連接名稱 36 self.cls._meta['db_alias'] = self.db_alias 37 # 將collection置為None,於是之后會重新根據新的db連接獲取collection 38 self.cls._collection = None 39 return self.cls 40 41 def __exit__(self, t, value, traceback): 42 """Reset the db_alias and collection.""" 43 # 退出with代碼塊后觸發 44 # 恢復為切換前的db連接名稱 45 self.cls._meta['db_alias'] = self.ori_db_alias 46 # 恢復為切換前的collection 47 self.cls._collection = self.collection