(動態模型類,我的獨創)Django的原生ORM框架如何支援MongoDB,同時應對客戶使用時隨時變動欄位

秦皇漢武發表於2022-04-04

1.背景知識

  需要開發一個系統,處理大量EXCEL表格資訊,各種類別。表格標題多變,因此使用不需要預先設計資料表結構的MongoDB,即NoSQL。一是欄位不固定,二是同名欄位可以儲存不同的欄位型別。

  同時,後端確定使用Django,原因是資料處理這一塊,python無敵於天下。

  Django採用MVT模式開發。MODEL是最關鍵的部分。是ORM的核心。但是ORM主要用於關係型資料庫。那麼如何解決?

2.大量調研的網上資料

(1)mongoengine

    mongoengine(跟pymongodb類似,但是相比於後者,又能提供模型類的功能,封裝一些資料操作,不用單獨寫一堆crub)

      缺點:經反覆測試研究,不支援django的原生後臺管理功能,因為無法將django的資料遷移到mongodb資料庫中因此無法使用原生的後臺管理介面,需要定製。

(2)djongo(注意不是django。爹是django,媽是mongodb,交配出來的物種)

       

 

       與Django支援的其它SQL資料庫型別,用於支援Nosql。

       可以將django的資料遷移到mongodb資料庫中,也可以使用原生的後臺管理介面

(3)Django-nonrel

  django的分支,有時間可以研究一下。

3.目前的問題(用mongodb仿ORM,那麼如何隨時變動欄位?)

 

   OVM模型的重點就是提前定義類的成員,同時遷移到資料表中,形成相應欄位。

        對於一個excel表,記錄圖書資訊,比如,書名,出版日期。

        我們只需要用下述方式定義一個類,

# 建立圖書類
class BookInfo(models.Model):
    """圖書模型類"""
    # 圖書名稱,CharField說明是該類屬性是一個字串,max_length指定最大長度
    book_title = models.CharField(max_length=20)

    # 出版日期,DateField說明該類屬性是一個日期
    book_pub_date = models.DateField()

    def __str__(self):
        """覆蓋對BookInfo例項化物件使用str()的返回值"""
        return self.book_title  # 返回書名

  作為model放入django中。後面執行如下操作,資料庫就可以相應的自動變化了。

       有djongo對django和mongodb支援,我們可以將mongodb“仿”為ORM模型,注意,是仿。

def get(request):
    # book=BookInfo()
    # book.book_title='水滸傳'
    # book.book_pub_date=date(1960,1,1)
    
    # book.save()
   #    return HttpResponse('helloworld')

  那麼如何隨時變動欄位呢?比如,哪天使用方的excel表格又變動了,增加了一列,比如作者。那麼如何將作者這個欄位加入呢?同時,不改變原始程式碼?

4.解決方案(動態模型類)

(1)模型save的侷限性

 首先,python支援類成員隨時定義,我們直接在模型類物件中,新增一個成員。 
# book.author = '羅貫中'
但是發現模型的save操作,對這個臨時增加的成員不處理,無法加入資料庫中。
save只處理在原始models.py檔案中定義BookInfo的時候定義的固定類成員。
通過除錯發現,Django在執行前需要首先對各個models.py進行解析。因此,後續在模型類例項化物件中臨時增加成員,是沒用的。

(2)我的方式:強制解析新的模型類,支援對欄位進行變動,更好時候NoSQL

 通過檔案定義的類,具有一些特殊欄位。__module__ __qualname和__doc__

   由於Django只處理models.py檔案中定義的模型類。所以,

   在程式執行時,動態建立新的模型類,並且修改其成員屬性,把其模擬為檔案中定義的模型類,然後再用django進行解析,使其能夠作為ORM的新model。

   

BookInfo = type('BookInfo', (models.Model,), {"__module__":"booktest.models",'__qualname__':'BookInfo',"__doc__":"註釋", "book_title": models.CharField(max_length=20),"book_pub_date" :models.DateField(),"author": models.CharField(max_length=20)})
book=BookInfo()
book.book_title='三國演義xxx'
book.book_pub_date=date(1960,1,1)
book.author='我是傑少啊'
book.save()

 注意兩點:第一,由於記憶體中有原始模型類BookInfo,我們要對其完全覆蓋掉。

   上面type為什麼會觸發django重新解析模型類呢?

   因為BookInfo繼承的model,核心是繼承Django的ModelBase類。當執行上述動態類定義過程時,就會觸發解析。

   通過上述處理,我們就能實現對資料庫插入作者欄位了。資料庫中內容如下:

 

 

 

 

 

   

相關文章