項目碰到要使用mongodb的場景,以前只聽過這一強大的文檔數據庫,但一直沒有真正使用過,參考一下項目中已有的使用代碼,是通過import mongoengine這一模塊實現python服務對db中collection的增刪查改。
mongoengine的項目網站http://mongoengine.org 中介紹到:
MongoEngine is a Document-Object Mapper (think ORM, but for document databases) for working with MongoDB from Python.
大意是,MongoEngine是一個針對在Python中方便使用MongoDB的文檔對象的映射器(類似ORM(Object Relational Mapping),但是是針對文檔數據庫)
參考已有的代碼時發現,代碼通過定義一個繼承mongoengine.Document(定義於mongoengine/document.py文件中)的Python類和db中的collection建立了映射關系,通過對類的操作即可實現對db中對應collection的操作。
例如UserInfo的類定義如下:
class UserInfo(Document): """ 用戶數據對象 """ meta = { 'db_alias': 'user', 'indexes': [ 'user_id', 'user_type' ] } user_type = IntField(default=USER_COOP) user_id = StringField(default='', max_length=64) nickname = StringField(detault='', max_length=16) ctime = DateTimeField(default=datetime.utcnow) mtime = DateTimeField(default=datetime.utcnow)
其中meta用於定義類的一些元信息,如db_alias代表要訪問的mongodb中具體的db名稱,indexes則定義索引(用處?)。
然而其中並沒有發現指定訪問的collection名稱的代碼,估計是根據某種特殊規則從類的信息推斷生成出來的,這引起了我的好奇,想要探究一番其生成原理。
通過遠程登錄上mongodb,使用"show collections"查看user db中的collection列表,發現了名叫user_info的collection,實際測試也確認UserInfo類查詢的具體數據來源於其中
通過進一步參考官方文檔,發現meta中可以通過指定"collection"的key-value對人工指明UserInfo類綁定的collection,然而上述代碼中並沒有用到這一機制。官方文檔中說到mongodb默認通過將Document子類的名稱轉換為小寫來作為db中對應collection的名稱:
By default, the MongoDB collection used to store documents created using a Document subclass will be the name of the subclass converted to lowercase. A different collection may be specified by providing collection to the meta dictionary in the class definition.
然而這樣的話UserInfo對應的名稱應該是userinfo,而不是user_info才對。
使用開源軟件的優勢果斷凸顯出來了,拜讀源碼,研究個清楚。
查找源碼發現mongoengine中的Document類中定義有類成員my_metaclass和__metaclass,其類型均為TopLevelDocumentMetaclass,collection名稱根據UserInfo自動生成的邏輯就在這里面。
# The __metaclass__ attribute is removed by 2to3 when running with Python3 # my_metaclass is defined so that metaclass can be queried in Python 2 & 3 my_metaclass = TopLevelDocumentMetaclass __metaclass__ = TopLevelDocumentMetaclass TopLevelDocumentMetaclass的定義在mongoengine/base/metaclasses.py中: class TopLevelDocumentMetaclass(DocumentMetaclass): """Metaclass for top-level documents (i.e. documents that have their own collection in the database. """ ...
針對collection名稱自動生成的邏輯就在其__new__函數之中,其會在meta中沒有collection字段時,根據以下代碼片段生成默認collection名稱:
# Set default collection name if 'collection' not in meta: meta['collection'] = ''.join('_%s' % c if c.isupper() else c for c in name).strip('_').lower()
看到這里就一切了然了,將name中的所有大寫字母轉換為小寫+'_'的形式(UserInfo->_user_info),而后strip兩邊的'_'(_user_info->user_info)