Django 全局获取request

前言

Django项目中使用到了Singal来进行操作日志的记录的操作,但需要在Signal中获取reqeust中的某些数据。因此,如果有一个全局的request,将会对之后的操作带来很大的方便。


一、能否直接pip安装?

django_middleware_global_request

pip install django-middleware-global-request

二、如何使用?

1.引入

在项目的setting.py文件的MIDDLEWARE添加中间件。
在这里插入图片描述

2.使用

代码如下(示例):

message_dict["args"]["user_id"] = get_request().user.id

三、如何实现的?

1、django Middleware原理

django在初始化时,会先加载middleware。

class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.load_middleware()
    # 子线程中调用
    def __call__(self, environ, start_response):
        request = self.request_class(environ)
        response = self.get_response(request)
class BaseHandler:
    def load_middleware(self, is_async=False):
            get_response = self._get_response_async if is_async else self._get_response
        	handler = convert_exception_to_response(get_response)
        	# 初始handler为Basehandler的_get_response方法。 
            for middleware_path in reversed(settings.MIDDLEWARE):
            	# 我们每个中间件
            	middleware = import_string(middleware_path)
            	# 初始adapted_handler 为Basehandler的_get_response方法。
            	adapted_handler = self.adapt_method_mode(
                    middleware_is_async, handler, handler_is_async,
                    debug=settings.DEBUG, name='middleware %s' % middleware_path,
                )
            	mw_instance = middleware(adapted_handler)
            	......
            	handler = adapted_handler
            	......
            	# 记录每个中间件的流程方法.
	            if hasattr(mw_instance, 'process_view'):
	                self._view_middleware.insert(...)
	            if hasattr(mw_instance, 'process_template_response'):
	                self._template_response_middleware.append(...)
	            if hasattr(mw_instance, 'process_exception'):
	                self._exception_middleware.append(...)
	        ......
	        self._middleware_chain = handler

在初始化时,会实例化每个middleware类。等到for循环结束时,handler = A(B(C(D(_get_response)))),将每个中间件层层嵌套起来。
在此过程中,会将中间件的process_view process_template_response process_exception 存储到列表中等待调用。
get_response(self, request)中调用,get_response(self, request)在子线程中被调用。

    def get_response(self, request):
    	...
        response = self._middleware_chain(request)
        ....
        return response

self._middleware_chain(request) 会层层调用中间件的__call__方法。
等到所有的中间件都调用完成之后,将调用BaseHandler._get_response方法,A(B(C(D(_get_response))))


    def _get_response(self, request):
    		# 便利调用所有中间件的process_view方法
            for middleware_method in self._view_middleware:
            	response = middleware_method(request, callback, callback_args, callback_kwargs)
            	if response:
            	    break
           	...
           	# 便利调用所有中间件的process_template_response 方法
           	if hasattr(response, 'render') and callable(response.render):
           		for middleware_method in self._template_response_middleware:
           			response = middleware_method(request, response)
            try:
                response = response.render()
            except Exception as e:
            	# 便利调用所有中间件的process_exception方法
                response = self.process_exception_by_middleware(e, request)
                if response is None:
                    raise

在_get_response中,会调用中间件的process_view process_template_response process_exception

再看一下MiddlewareMixin的调用

class MiddlewareMixin:
    def __call__(self, request):
		......
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        response = response or self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response

MiddlewareMixin.__call__在上面被调用。
可以看到,中间件的执行顺序为:process_request>process_view>process_template_response (看情况)>process_exception(有异常)>process_response

2、中间件是线程隔离的吗?

通过上面的代码,我们能看到,中间件只会在项目初始化时,有WSGIHandler初始化一次。并不是每个线程进来都会初始化。
因此需要解决线程冲突问题。

3、django_middleware_global_request实现原理

3.1 GlobalRequestMiddleware调用顺序

class GlobalRequestMiddleware(object):

    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        with GlobalRequest(request=request):
            return self.get_response(request)
class GlobalRequestStorage(object):
    storage = threading.local()

    def get(self):
        if hasattr(self.storage, "request"):
            return self.storage.request
        else:
            return None

    def get_user(self, request=None):
        request = request or self.get()
        return getattr(request, "user", None)

    def set(self, request):
        self.storage.request = request

    def set_user(self, user, request=None):
        if request:
            self.storage.request = request
        if not hasattr(self.storage, "request"):
            self.storage.request = HttpRequest()
        if user:
            self.storage.request.user = user

    def recover(self, request=None, user=None):
        if hasattr(self.storage, "request"):
            del self.storage.request
        if request:
            self.storage.request = request
            if user:
                self.storage.request.user = user
class GlobalRequest(object):

    def __init__(self, request=None, user=None):
        self.global_request_storage = GlobalRequestStorage()
        self.new_request = request or HttpRequest()
        self.new_user = user
        self.old_request = self.global_request_storage.get()
        self.old_user = self.global_request_storage.get_user(self.old_request)

    def __enter__(self):
        self.global_request_storage.set_user(user=self.new_user, request=self.new_request)
        return self.global_request_storage.get()

    def __exit__(self, *args, **kwargs):
        self.global_request_storage.recover(request=self.old_request, user=self.old_user)

GlobalRequest 使用上下文管理request。
__init__时,实例化GlobalRequestStorage
__enter__ 时,将当前的request存储到GlobalRequestStorage()中,
__exit__时,将GlobalRequestStoragedel self.storage.request

3.2 如何实现线程隔离的?

线程隔离:threading.local()

threading.local的作用:为每个线程开辟一块空间进行数据的存储
空间与空间之间数据是隔离的

类似于Flask框架的实现。Flask框架也是借用了threading.local()在处理线程隔离,Flask中维护了一个字典,来存储app、request、g等。

def get_request():
    return GlobalRequestStorage().get()

get_request()中又新实例化了一个GlobalRequestStorage,但storage = threading.local()将会从当前线程的数据块中获取数据。


参考文献

threading.local
Django系列之中间件加载原理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值