Django之MVC(MTV)设计模式

Django是MVC吗?

首先说说Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求,其示意图如下所示:

Django之MVC(MTV)设计模式_第1张图片

Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:

M 代表模型(Model):负责业务对象和数据库的关系映射(ORM)。

T 代表模板 (Template):负责如何把页面展示给用户(html)。

V 代表视图(View):负责业务逻辑,并在适当时候调用Model和Template。

除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template,MTV的响应模式如下所示:Django之MVC(MTV)设计模式_第2张图片

1,Web服务器(中间件)收到一个http请求

2,Django在URLconf里查找对应的视图(View)函数来处理http请求

3,视图函数调用相应的数据模型来存取数据、调用相应的模板向用户展示页面

4,视图函数处理结束后返回一个http的响应给Web服务器

5,Web服务器将响应发送给客户端

这种设计模式关键的优势在于各种组件都是松耦合的。这样,每个由 Django驱动的Web应用都有着明确的目的,并且可独立更改而不影响到其它的部分。

比如,开发者更改一个应用程序中的 URL 而不用影响到这个程序底层的实现。设计师可以改变 HTML页面的样式而不用接触Python代码。

数据库管理员可以重新命名数据表并且只需更改模型,无需从一大堆文件中进行查找和替换。

落到实处,Django的MTV模式相对应的python文件如下:

Django之MVC(MTV)设计模式_第3张图片

Django之MVC(MTV)设计模式_第4张图片 Django之MVC(MTV)设计模式_第5张图片

Django是如何工作的?

要真正的欣赏Django,你需要撇开表象来看本质。它启发你得同时也让会让你不知所措。下图显示了在Django应用中一个典型的web请求是如何被处理的。

Django之MVC(MTV)设计模式_第6张图片

前面的图片展示了从一个访客的浏览器到Django应用并返回的一个web请求的简单历程。如下是数字标识的路径:

  1. 浏览器发送请求(基本上是字节类型的字符串)到web服务器。
  2. web服务器(比如,Nginx)把这个请求转交到一个WSGI(比如,uWSGI),或者直接地文件系统能够取出一个文件(比如,一个CSS文件)。
  3. 不像web服务器那样,WSGI服务器可以直接运行Python应用。请求生成一个被称为environ的Ptyhon字典,而且,可以选择传递过去几个中间件的层,最终,达到Django应用。
  4. URLconf中含有属于应用的urls.py选择一个视图处理基于请求的URL的那个请求,这个请求就已经变成了HttpRequest——一个Python字典对象。
  5. 被选择的那个视图通常要做下面所列出的一件或者更多件事情:
    1. 通过模型与数据库对话。
    2. 使用模板渲染HTML或者任何格式化过的响应。
    3. 返回一个纯文本响应(不被显示的)。
    4. 抛出一个异常。
  6. HttpResponse对象离开Django后,被渲染为一个字符串。
  7. 在浏览器见到一个美化的,渲染后的web页面。

虽然某些细节被省略掉,这个解释应该有助于欣赏Django的高级架构。它也展示了关键的组件所扮演的角色,比如模型,视图,和模板。Django的很多组件都基于这几个广为人知设计模式。

命令模式

在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合,这就是命令模式(Command Pattern)。

命令模式有以下特点:

  1. 命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。
  2. 每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。
  3. 命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
  4. 命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
  5. 命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

在Django中,把get和post这些请求的行为抽象成一个HttpRequest类,我们可以通过request实例的method获取行为的类型:

if request.method == 'GET':
    do_something()
elif request.method == 'POST':
    do_something_else()

Django把返回的行为抽象成HttpResponse类,通过调用HttpResponse的方法

from django.http import HttpResponse
>>> response = HttpResponse("Here's the text of the Web page.")
>>> response = HttpResponse("Text only, please.", content_type="text/plain")

此外,对于不同的数据,Django还分别抽象了相应的类来处理。

Json数据:

from django.http import JsonResponse
>>> response = JsonResponse({'foo': 'bar'})
>>> response.content
b'{"foo": "bar"}'

File数据:

from django.http import FileResponse
>>> response = FileResponse(open('myfile.png', 'rb'))

观察者模式

观察者模式(有时又被称为发布(publish)-订阅(Subscribe)模式、模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

Django中,存在Singal与dispatch类,分别用来发送和接受信号,实现被观察与观察者的角色。

首先,我们可以定义一个观察者,接收一种特定的信号:

import django.dispatch

pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

然后我们可以发送信号,使观察者接收相应的信息:

class PizzaStore(object):
    ...

    def send_pizza(self, toppings, size):
        pizza_done.send(sender=self.__class__, toppings=toppings, size=size)
        ...

此外,Django中也有很多built in的signal类,实现不同的功能。

  • django.db.models.signals.pre_save & django.db.models.signals.post_save

当一个model执行save()方法,即在model对应的数据库中的一张表插入一条数据的前后发送信号。

  • django.db.models.signals.pre_delete & django.db.models.signals.post_delete

当一个model执行delete()方法,即在model对应的数据库中的一张表删除一条数据的前后发送信号。

  • django.db.models.signals.m2m_changed

当model中的一个多对多的关系发生改变时发送信号。

  • django.core.signals.request_started & django.core.signals.request_finished

当Django开始或者完成一个HTTP请求的时候发送信号。

Django RestframWork中的REST设计

在使用Django框架的时候,也许有些追求ROA的码农们可能会吐槽没有REST化支持。在Django Restframework框架中的APIView类中,提供了put(), delete(), patch()方法的支持。

比如,如果你想上传一个文件,对应的view就可以这样写:

class FileUploadView(views.APIView):
    parser_classes = (FileUploadParser,)

    def put(self, request, filename, format=None):
        file_obj = request.data['file']
        # ...
        # do some stuff with uploaded file
        # ...
        return Response(status=204)

此外,如果你想再服务器端发送请求,Django Restframework中APIClient类提供了各种方法,get(), post(), put(), patch(), delete(), head() and options()。

from rest_framework.test import APIClient

client = APIClient()
client.post('/notes/', {'title': 'new idea'}, format='json')

总的来说,Django提供的框架使得代码量减少了很多,使得程序员只需关注核心的业务逻辑。更多关于Django的设计原则与最佳实践,在以后的学习中逐渐添加吧。

你可能感兴趣的:(Django)