首先帶你們瞭解一下什麼是MVVM模式:java
什麼是MVVM?MVVM是Model-View-ViewModel的縮寫。python
MVVM是MVC的加強版,實質上和MVC沒有本質區別,只是代碼的位置變更而已
從名字上看,MVVM比MVC架構中多了一個ViewModel,沒錯,就是這個ViewModel,他是MVVM相對於MVC改進的核心思想。在開發過程當中,因爲需求的變動或添加,項目的複雜度愈來愈高,代碼量愈來愈大,此時咱們會發現MVC維護起來有些吃力,首先被人吐槽的最多的就是MVC的簡寫變成了Massive-View-Controller(意爲沉重的Controller)linux因爲Controller主要用來處理各類邏輯和數據轉化,複雜業務邏輯界面的Controller很是龐大,維護困難,因此有人想到把Controller的數據和邏輯處理部分從中抽離出來,用一個專門的對象去管理,這個對象就是ViewModel,是Model和Controller之間的一座橋樑。當人們去嘗試這種方式時,發現Controller中的代碼變得很是少,變得易於測試和維護,只須要Controller和ViewModel作數據綁定便可,這也就催生了MVVM的熱潮。android
你們都知道Django是MVT模式,Model就是View和Template/Interface之間的數據傳遞的「信使」,這種模式存在一個問題,就是當咱們的業務不斷擴大以後須要在接口返回出model裏不包含的數據時該怎麼辦?例如一個商店,咱們要動態計算它距離咱們當前位置有多遠,那麼這個距離確定是不包含在Model裏面的,數據庫也不可能實時存儲這類數據。數據庫
那麼這時候咱們就須要在Model上,再加上一層ViewModel,顧名思義,視圖模型,是用來在視圖裏傳遞和處理數據的模型。c#
在App包下面建立一個view_models
文件,內容以下:架構
from rest_framework.request import Request from core.models import Store from core.serializers import StoreSerializer class StoreViewModel: def __init__(self, store: Store, distance=0.0, request: Request = None): self.store = store self.distance = distance self.request = request @property def serialize_data(self): return StoreSerializer(self.store, context={ 'distance': self.distance, 'request': self.request, }).data
上面的代碼定義了一個商店的視圖模型,構造方法中除了咱們的Model對象,還有Model中不包括的distance參數,還有一個request用來傳遞請求的context,這個在Drf中是很重要的,若是不處理好context的傳遞,會致使Drf在序列化一些文件或者連接類字段的時候丟失前半部分的域名。測試
接下來看看serialize_data
這個屬性,它作的工做很簡單,就是把Model對象傳給序列化器,而後在context中存入咱們的額外參數distance和request。設計
再來看看序列化器要如何改造以適應ViewModel模型。rest
class StoreSerializer(serializers.ModelSerializer): distance = serializers.SerializerMethodField() class Meta: model = models.Store fields = '__all__' def get_distance(self, obj: models.Store): return self.context.get('distance', 0)
這裏能夠看到序列化器中,我是把額外的distance字段處理成SerializerMethodField
,而後在get_distance
方法中實現,經過self.context
屬性能夠獲取到咱們在ViewModel中傳入的context,這樣就實現額外參數的序列化。
最後咱們在看看在View,也就是控制器,看看如何將ViewModel和本來的分頁,權限各種功能結合在一塊兒。
class StoreViewSet(viewsets.ReadOnlyModelViewSet): """商家相關功能""" serializer_class = serializers.StoreSerializer queryset = models.Store.objects.all() @action(detail=False) def location(self, request): """根據地理位置篩選商家""" city = request.GET.get('city') town = request.GET.get('town') lat = request.GET.get('lat') lng = request.GET.get('lng') # 根據城市、區鎮篩選商店 queryset = models.Store.objects.filter(city=city, town=town) # 調用接口計算全部商店距離當前位置的距離,該接口返回ViewModel store_view_models = tencent_map.stores_distance(from_lat=lat, from_lng=lng, queryset=queryset, request=request) # 對ViewModelSet進行排序,按照距離 store_view_models.sort(key=lambda store_view_model: store_view_model.distance) # 使用列表生成器,對每一個ViewModel進行序列化 stores_data = [store_vm.serialize_data for store_vm in store_view_models] # 對結果數據進行分頁 page = self.paginate_queryset(stores_data) return self.get_paginated_response(page)
上面的代碼目前在開發環境運行良好,我已經寫了詳細的註釋了,能夠看到用ViewModel模式是能夠和本來的ViewSet很好的結合在一塊兒的,包括分頁這些功能均可以正常使用。
標題中我用了「強行」這個詞,就是以爲我這樣實現好像很不優雅,但又不至於hack,由於這個需求很簡單,只要實現了就行,我也尚未去搜索其餘的解決方案,在本文中提出了個人ViewModel與Django結合解決方案,若是你們有更好的解決方案能夠留言一塊兒探討~
我整理了一系列的技術文章和資料,在公衆號「程序設計實驗室」後臺回覆 linux、flutter、c#、netcore、android、java、python 等可獲取相關技術文章和資料,同時有任何問題均可以在公衆號後臺留言~