扫盲

首先带大家了解一下什么是MVVM模式:

什么是MVVM?MVVM是Model-View-ViewModel的缩写。

MVVM是MVC的增强版,实质上和MVC没有本质区别,只是代码的位置变动而已

从名字上看,MVVM比MVC架构中多了一个ViewModel,没错,就是这个ViewModel,他是MVVM相对于MVC改进的核心思想。在开发过程中,由于需求的变更或添加,项目的复杂度越来越高,代码量越来越大,此时我们会发现MVC维护起来有些吃力,首先被人吐槽的最多的就是MVC的简写变成了Massive-View-Controller(意为沉重的Controller)

由于Controller主要用来处理各种逻辑和数据转化,复杂业务逻辑界面的Controller非常庞大,维护困难,所以有人想到把Controller的数据和逻辑处理部分从中抽离出来,用一个专门的对象去管理,这个对象就是ViewModel,是Model和Controller之间的一座桥梁。当人们去尝试这种方式时,发现Controller中的代码变得非常少,变得易于测试和维护,只需要Controller和ViewModel做数据绑定即可,这也就催生了MVVM的热潮。

引言

大家都知道Django是MVT模式,Model就是View和Template/Interface之间的数据传递的「信使」,这种模式存在一个问题,就是当我们的业务不断扩大之后需要在接口返回出model里不包含的数据时该怎么办?例如一个商店,我们要动态计算它距离我们当前位置有多远,那么这个距离肯定是不包含在Model里面的,数据库也不可能实时存储这类数据。

那么这时候我们就需要在Model上,再加上一层ViewModel,顾名思义,视图模型,是用来在视图里传递和处理数据的模型。

简单实现

在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模型。

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 等可获取相关技术文章和资料,同时有任何问题都可以在公众号后台留言~

Python后端日常操作之在Django中「强行」使用MVVM设计模式的更多相关文章

  1. redis的字符串操作以及在django中的使用

    redis ----redis.MongoDB : 非关系型数据库 redis   存储在内存中 MongoDB 存储在硬盘中 l  简介 redis是一个key-value存储系统 , 支持持久化 ...

  2. python连接redis、redis字符串操作、hash操作、列表操作、其他通用操作、管道、django中使用redis

    今日内容概要 python连接redis redis字符串操作 redis之hash操作 redis之列表操作 redis其他 通用操作,管道 django中使用redis 内容详细 1.python ...

  3. Django中使用mysql数据库并使用原生sql语句操作

    Django自身默认使用sqlite3这个轻量级的数据库,但是当我们开发网站时,sqlite3就没有mysql好,sqlite3适合一些手机上开发使用的数据库. 准备的软件mysql数据库,版本5.7 ...

  4. Django中cookie和session的操作

    一.cookie和session cookie:在网站中,http请求是无状态的.也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户.cookie的出现就是 ...

  5. 137.在Django中操作session

    在Django中操作session 在django中session默认情况下是存储在服务器的数据库中的,在表中会根据sessionid来提取指定的session数据,然后再把这个sessionid放到 ...

  6. Django中的模型(操作数据库)

    目录 Django配置连接数据库 在Django中操作数据库 原生SQL语句操作数据库 ORM模型操作数据库 增删改查 后台管理 使用后台管理数据库 模型是数据唯一而且准确的信息来源.它包含您正在储存 ...

  7. python日志轮转RotatingFileHandler在django中的一个bug

    简介 大量过时的日志会占用硬盘空间,甚至长时间运行不注意会占满硬盘导致宕机,那么就可以使用内建logging模块根据文件大小(logging.handlers.RotatingFileHandler) ...

  8. django中的跨表查询梳理

    1.前言 最近在写一个小项目,里面主要涉及的就是表与表之间复杂的关系.当真正开发起来的时候,才发现自己对复杂的表关系间的查询有点混乱,趁着这几天的时间,重新梳理了一下. 2.概念 在开始之前,先明确几 ...

  9. 浅谈 MVVM 设计模式在 Unity3D 中的设计与实施

    初识 MVVM 谈起 MVVM 设计模式,可能第一映像你会想到 WPF/Sliverlight,他们提供了的数据绑定(Data Binding),命令(Command)等功能,这让 MVVM 模式得到 ...

随机推荐

  1. vue基础入门(4)

    4.综合实例 4.1.基于数据驱动的选项卡 4.1.1.需求 需求说明: 1. 被选中的选项按钮颜色成橙色 2. 完成被选中选项下的数据列表渲染 3. 完成选项切换 4.1.2.代码实现 <!D ...

  2. 搭建nginx服务器nginx-1.6.2.tar.gz

    2016-06-17 09:06:52   一.实验环境 CentOS6.5 软件:nginx-1.6.2.tar.gz 二.实验步骤 1)安装nginx所依赖的软件 yum -y install p ...

  3. Apache POI 操作Excel(2)-- POI包引入项目

    Apache POI发行版包含对许多文档文件格式的支持.这种支持在几个Jar文件中提供.并非每种格式都需要所有jar.下表显示了POI组件.Maven存储库标记和项目的Jar文件之间的关系. (htt ...

  4. Python之浅谈函数

    目录 文件的高级应用 文件修改的两种方式 第一种 第二种 函数的定义 函数的参数 函数的返回值 文件的高级应用 r+即可读又可写,并且是在后面追加 w+清空文件的功能是w提供的 a+a有追加的功能,a ...

  5. Java设计模式三

    建造者模式 当我们思考通过复杂的零件来生成一个完整的产品时,就用到了今天要说的主题-建造者模式,下面我们实际的代码来分析建造者模式的设计 假设飞机起飞需要有多个步骤,但是每种型号的飞机起飞的步骤又不相 ...

  6. 比Minikube更快,使用Kind快速创建K8S学习环境

    简述 K8S 如火如荼的发展着,越来越多人想学习和了解 K8S,但是由于 K8S 的入门曲线较高很多人望而却步. 然而随着 K8S 生态的蓬勃发展,社区也呈现了越来越多的部署方案,光针对生产可用的环境 ...

  7. web页面的重构和回流【转载】

    在了解什么是重构和回流之前,我们应该先看看浏览器是怎么渲染的? 浏览器的渲染过程: 1.处理HTML脚本,生成DOM树(DOM树里包含所有的HTML标签,包括display:none和js动态添加的元 ...

  8. 控制流程之while循环, for循环

    条件循环:while,语法如下 while 条件: # 循环体 # 如果条件为真,那么循环体则执行,执行完毕后再次循环,重新判断条件... # 如果条件为假,那么循环体不执行,循环终止死循环 基本使用 ...

  9. 【XCTF】ics-05

    信息: 题目来源:XCTF 4th-CyberEarth 标签:PHP.伪协议 题目描述:其他破坏者会利用工控云管理系统设备维护中心的后门入侵系统 解题过程 题目给了一个工控管理系统,并提示存在后门, ...

  10. JavaScript学习 Ⅳ

    八. 批量创建对象 使用工厂方法创建对象 function creatPerson(name, age, gender='男'){ var obj = new Object(); obj.name = ...