首页 > 其他分享 >Django中间件案例由浅入深+实战

Django中间件案例由浅入深+实战

时间:2023-06-15 15:45:29浏览次数:70  
标签:由浅入深 get 中间件 middleware request Django response

Django中间件案例由浅入深+实战

原文链接:https://pfertyk.me/2020/04/getting-started-with-django-middleware/

Django具有许多有用的功能特色, 其中之一便是中间件。在本文中,我将简要解释中间件如何工作以及如何开始编写自己的中间件。

这篇文章中包含的源代码可在GitHub上找到。

一般概念

中间件允许您在一个浏览器的请求在到达Django视图之前处理它,以及在视图返回的响应到达浏览器之前处理这个响应。Django为每个项目保留一个中间件列表。您可以在设置文件settings.py中找到它MIDDLEWARE。每个新的Django项目已经在该列表中添加了一堆中间件,在大多数情况下,您不应从该列表中删除任何中间件。但是,您可以添加自己的。

中间件的应用顺序与添加到Django设置列表中的顺序相同。浏览器发送请求时,将按以下方式处理:

Browser -> M_1 -> M_2 -> ... -> M_N -> View

视图接收请求,执行一些操作,然后返回响应。在进入浏览器的过程中,响应必须再次通过每个中间件,但顺序相反:

Browser <- M_1 <- M_2 <- ... <- M_N <- View

这是一个非常简短的解释。更详细的描述可以在Django文档中找到 。

一个简单的例子

我们将从一个简单的中间件例子开始,该中间件就算处理请求所花费的时间。这篇文章中的所有示例都使用Django 3.0.5和Python 3.6.9。

项目设置

首先,使用单个应用程序创建一个Django项目。忽略数据库迁移,因为本文中的示例将不使用数据库。在您的应用程序app目录下创建一个名为middleware.py的文件,这是我们放置大多数代码的地方。

django-admin startproject django_middleware
cd django_middleware
python manage.py startapp intro
touch intro/middleware.py

不要忘记在django_middleware/settings.py中注册您的应用程序(app):

编写Django中间件

根据Django文档,有两种编写中间件的方法:函数和类。我们将使用第一种方法,但最后一个示例还将向您展示如何使用类编写中间件。

Django中间件的一般结构如下所示(示例取自Django文档):

def simple_middleware(get_response):
    # 一次性设置和初始化
    def middleware(request):
        # 请求在到达视图前执行的代码
        response = get_response(request)
        # 响应在返回给客户端前执行的代码
        return response
    return middleware

当Django初始化中间件并将其添加到项目所有中间件的列表中时,该函数simple_middleware将被调用一次。服务器处理每个请求都会调用middleware该函数。当请求从浏览器发送到服务器时,将调用response = get_response(request)该行之前的所有内容。当响应从服务器返回到浏览器时,将调用response = get_response(request)此行之后的所有内容。

那么这条分界线respone = get_response(request)做什么的?简而言之,它将调用列表中的下一个中间件。如果这是最后一个中间件,则将调用该视图。该视图接收请求,执行一些操作并生成响应。然后将该响应返回到列表中的最后一个中间件,然后向上执行直到不再有中间件时将该响应发送到浏览器。

注意:settings.py里中间件的顺序很重要。更多中间件知识见:

在我们的示例中,我们要检查处理请求的整个过程需要多长时间。像这样编辑文件intro/middleware.py:

import time
 
def timing(get_response):
    def middleware(request):
        t1 = time.time()
        response = get_response(request)
        t2 = time.time()
        print("TOTAL TIME:", (t2 - t1))
        return response
    return middleware

在此示例中,我们以秒为单位(time.time())测量请求前后的时间,并打印出时间差异。

下一步是安装中间件,以使Django知道我们将使用它。我们要做的就是将其添加到django_middleware/settings.py

MIDDLEWARE  =  [ 
    'intro.middleware.timing' ,
    ... 
]

注意:在此示例中,intro是Django应用程序middleware 的名称,是包含我们的代码的Python文件timing的名称,并且是该文件中的中间件函数的名称。

现在我们准备测试它。打开浏览器并导航到localhost:8000。在浏览器中,您应该看到默认的Django项目页面(带有火箭的页面)。在命令行(您在其中调用p``ython manage.py runserver)中,您应该看到类似以下内容:

TOTAL TIME: 0.0013387203216552734
[04/Apr/2020 17:15:34] "GET / HTTP/1.1" 200 16351

修改请求

我们的中间件效果很好,可以将信息打印到命令行。但是我们可以走得更远:如何向请求中添加内容,以便我们的视图或模板以后可以使用它?由于我们从事计时业务,如何添加请求发生的日期和时间?

此修改将非常容易。intro/middleware.py像这样编辑文件:

import time
import datetime
 
def timing(get_response):
    def middleware(request):
        request.current_time = datetime.datetime.now()
        t1 = time.time()
        response = get_response(request)
        t2 = time.time()
        print("TOTAL TIME:", (t2 - t1))
        return response
    return middleware

我们添加了2行:import datetimerequest.current_time = datetime.datetime.now()。他们一起将当前时间添加到我们的请求对象中。现在,我们需要一个视图来显示该时间。编辑intro/views.py

from django.http import HttpResponse
 
def showtime(request):
    return HttpResponse('Request time is: {}'.format(request.current_time))

对于这样一个简单的示例,我们不需要模板,我们可以直接在代码中创建HttpResponse对象。

现在,我们需要一个URL作为视图。创建一个文件intro/urls.py并编辑它:

from django.urls import path
from .views import showtime
 
urlpatterns = [
    path('', showtime),
]

记住也要编辑django_middleware/urls.py

from django.contrib import admin
from django.urls import include, path
 
urlpatterns = [
    path('', include('intro.urls')),
    path('admin/', admin.site.urls),
]

多次刷新页面,以检查您是否将获得不同的结果(每次请求的时间应有更新)。

更有用的东西:处理异常

现在该举一个更有趣的例子了。考虑一下这种现实情况:您编写了一个程序,但它不起作用。发生在我们当中最好的,别担心。那你通常做什么?您是否会搜索Stackoverflow上的答案?您可能会这样做,实际上所有编码人员都这样做。我们如何创建可以为我们进行搜索的中间件呢?

Django中间件可以包含一个在每次引发异常时都会调用的函数。该函数process_exception被调用,并带有两个参数:引起异常的请求(request)和异常本身(exception)。

如果我们将中间件定义为一个函数,那么我们可以process_exception 像这样实现:

def simple_middleware(get_response):
    def middleware(request):
        return get_response(request)
 
    def process_exception(request, exception):
        # Do something useful with the exception
        pass
 
    middleware.process_exception = process_exception
    return middleware

在我们的案例中,我们想将异常发送到StackOverflow并获取最相关问题答案的链接。

API简介

如果您以前没有使用过API,请不要担心。总体思路是:就像您使用网络浏览器将问题发送到Internet一样,API是通过代码向网站自动发送问题的一种方式。

Stack Exchange提供了用于查询其网站问题的API。基本url为https://api.stackexchange.com/2.2/search,之后可以放入搜索参数。所以,如果你想查询标签上有python,标题中有django的StackOverflow上3个top搜索结果(按票数排序),你可以发送这样的请求:https://api.stackexchange.com/2.2/search?site=stackoverflow&pagesize=3&sort=votes&order=desc&tagged=python&intitle=django。继续并在浏览器中检查它。您应该会看到以下内容:

在Python中,要发送这样的请求,我们将使用一个第三方包,名为 requests

Stackoverflow中间件

让我们创建一个新的中间件,名为stackoverflow

import requests
from django.http import HttpResponse
 
# Previous imports and timing middleware should remain unchanged
def stackoverflow(get_response):
    def middleware(request):
        # This method does nothing, all we want is exception processing
        return get_response(request)
 
    def process_exception(request, exception):
        url = 'https://api.stackexchange.com/2.2/search'
        params = {
            'site': 'stackoverflow',
            'order': 'desc',
            'sort': 'votes',
            'pagesize': 3,
            'tagged': 'python;django',
            'intitle': str(exception),
        }
        response = requests.get(url, params=params)
        html = ''
        for question in response.json()['items']:
            html += '<h2><a href="{link}">{title}</a></h2>'.format(**question)
        return HttpResponse(html)
 
    middleware.process_exception = process_exception
 
    return middleware

每当视图引发异常时,process_exception都会调用我们的方法。我们使用该requests模块来调用Stack Exchange API。大多数参数是不言自明的。它们与我们在浏览器示例中使用的相同,但不是让它们全部手动放置在URL中,而是让requests模块为我们完成。我们只是更改了标签(以搜索Python和Django),然后将异常用作字符串(str(exception))来搜索可用问题的标题。得到Stack Overflow的响应后,我们将HTML放在一起,其中包含指向每个相关问题的链接。希望我们可以在那里找到解决问题的答案。最后,该HTML返回到浏览器。

请注意,来自Stack Overflow的响应不是正常的网页,而是一堆名为JSON格式的信息。这就是为什么我们使用response.json()获得我们的结果。

当然,我们需要注册这个新的中间件:

MIDDLEWARE = [
    'intro.middleware.stackoverflow',
    'intro.middleware.timing',
    ...
]

我们现在唯一的问题是我们的views工作起来很顺利,没有异常发生。如果我们希望新的中间件要处理一些异常,则需要对其稍作修改。编辑intro/views.py

def showtime(request):
    raise Exception('Django middleware')
    # return HttpResponse('Request time is: {}'.format(request.current_time))

我们刚创建的中间件比初始示例要复杂一些。随着代码的增长,将中间件作为类而非函数进行管理可能是一个更好的主意。我们现在以类重写Stack Overflow中间件,如下所示:

class StackOverflow():
    def __init__(self, get_response):
        self.get_response = get_response
 
    def __call__(self, request):
        return self.get_response(request)
 
    def process_exception(self, request, exception):
        url = 'https://api.stackexchange.com/2.2/search'
        params = {
            'site': 'stackoverflow',
            'order': 'desc',
            'sort': 'votes',
            'pagesize': 3,
            'tagged': 'python;django',
            'intitle': str(exception),
        }
        response = requests.get(url, params=params)
        html = ''
        for question in response.json()['items']:
            html += '<h2><a href="{link}">{title}</a></h2>'.format(**question)
        return HttpResponse(html)

大多数代码看起来相似,但是对于一个类,我们需要将get_response 回调存储在我们的实例中,并在每个__call__方法调用中使用它。如果您喜欢此版本,请不要忘记更改设置:

MIDDLEWARE = [
    'intro.middleware.StackOverflow',
    ...
]

标签:由浅入深,get,中间件,middleware,request,Django,response
From: https://www.cnblogs.com/mengdie1978/p/17483054.html

相关文章

  • 基于中间件实现MySQL读写分离
    1.场景在数据库读多写少的时候2.读写分离只在主服务器上写,只在从服务器上读。使用amoeba中间件3.部署3.1Dockercompose一键部署Compose文件:version:'3.8'services:mysql_master:image:mysql:8.0.33-oracle......
  • django 实现linux运维管理平台
    概述使用django实现一个linux运维管理平台,可以实现注册登录,机器管理,服务器批量操作,服务器性能监控。详细一、准备工作1、需要准备什么环境我是在自己的云服务上编写和运行的,centos7.2的环境,python是系统自带的python2.7.5,django安装的1.8.5版本的。数据库使用的mysql......
  • How to Render Django Form Manually
    DealingwithuserinputisaverycommontaskinanyWebapplicationorWebsite.ThestandardwaytodoitisthroughHTMLforms,wheretheuserinputsomedata,submitittotheserver,andthentheserverdoessomethingwithit.Now,thechancesareth......
  • django 更改了modules.py 数据库模型,但是 python3 manage.py makemigrations 提示无
    现象:明明改了modules.py文件。删了appname/migrations/下所有内容。而且也删除了django模型变更记录表django_migrations中appname项目的记录 原因:删多了: appname/migrations/下所有内容。__init__.py不能删,需要重新创建一个,否则识别不了包了  ......
  • MySQL-ProxySQL中间件
    ProxySQL是基于MySQL的一款开源的中间件的产品,是一个灵活的MySQL代理层,可以实现读写分离,支持Query路由功能,支持动态指定某个SQL进行缓存,支持动态加载配置信息(无需重启ProxySQL服务),支持故障切换和SQL的过滤功能。相关ProxySQL的网站:https://www.proxysql.com/https://gith......
  • django中设置字段动态默认时间的四种方式
    1.create_time_one=models.DateTimeField('创建时间',default=datetime.now())2.update_time_one=models.DateTimeField('更新时间',default=datetime.now)3.create_time_tow=models.DateTimeField('创建时间',auto_now_add=True)4.update_ti......
  • 【Django学习笔记】-环境搭建
    对于初学django新手,根据以下步骤可以快速进行Django环境搭建虚拟环境创建使用virtualenv创建并启用虚拟机环境,关于virtualenv可参考https://www.yuque.com/binowen/tn8zvy/insclepipinstallvirtualenvvirtualenv./envsourceenv/bin/activate创建project1.使用pipinstall安装......
  • 手写 Django orm反向迁移 MySQL
    importpymysql,os####settingsdb={'NAME':'','USER':'','PASSWORD':'','HOST':'','PORT':'',}table_name_list=[]#表名列表......
  • django views 序列化
      RESTframework中的序列化类与Django的Form和ModelForm类非常相似。我们提供了一个Serializer类,它提供了一种强大的通用方法来控制响应的输出,以及一个ModelSerializer类,它为创建处理模型实例和查询集的序列化提供了有效的快捷方式。Serializers 序列化器允许把像查询......
  • Django学习之路
    Django学习之路基于Jason的视频,收益匪浅.谢谢Jason的教诲.1、Django前戏2、Django基础3、Django路由层4、Django视图层......