Django实现一个简单的中间件,不熟悉中间件的爬坑之路

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012561176/article/details/84869330

1、在之前,写过一篇文章,自定义一个简单的中间件,文章链接如下:https://blog.csdn.net/u012561176/article/details/84024073

后面,发现还是有问题:

from django.views import View
from django.utils.decorators import method_decorator
 
@method_decorator(LoginMiddleware, name='dispatch')
class AddUser(View):
    def get(self, request, id):

如上面代码,如果一个View底下的get方法带一个参数或者多个参数的话,将会报错:

__call__() got an unexpected keyword argument 'id'

2、这个__call__()方法是中间件MiddlewareMixin里的方法,自定义中间件的时候可以重写的,在你本地安装路径底下的一个py文件:D:/Python 3.6.3/Lib/site-packages/django/utils/deprecation.py,MiddlewareMixin代码如下:

class MiddlewareMixin:
    def __init__(self, get_response=None):
        self.get_response = get_response
        super().__init__()

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

3、为了解决这个问题,报错说__call__()方法缺少参数,因此我给它加多一个参数,在新建一个中间件,接着调用这个代码如下:

class LoginMiddleware2(MiddlewareMixin):
    """
    自定义权限校验中间件,带多个参数时调用
    """
    def __call__(self, request, *args, **kwargs):
        return super().__call__(request)

    def process_request(self, request):
        """
        session是否登录校验检查
        :param request:
        :return:
        """
from django.views import View
from django.utils.decorators import method_decorator
 
@method_decorator(LoginMiddleware2, name='dispatch')
class AddUser(View):
    def get(self, request, id):

结果还是报错了,报错如下:

get() missing 1 required positional argument: 'id'

4、说这个get方法缺少一个参数id,后面就一直断点底层的东西,发现参数有带过去呀,怎么一直到dispatch那里走进去就报错了,发现是不是这个方法修饰器有问题,还是dispatch方法不行,后面试了好多方法,最后决定还是用别的修饰器看能不能成功,于是打开这个目录底下的一个decorators.py文件:D:/Python 3.6.3/Lib/site-packages/django/utils/decorators.py,代码如下:

"Functions that help with dynamically creating decorators for views."

# For backwards compatibility in Django 2.0.
from contextlib import ContextDecorator  # noqa
from functools import WRAPPER_ASSIGNMENTS, update_wrapper, wraps


class classonlymethod(classmethod):
    def __get__(self, instance, cls=None):
        if instance is not None:
            raise AttributeError("This method is available only on the class, not on instances.")
        return super().__get__(instance, cls)


def method_decorator(decorator, name=''):
    """
    Convert a function decorator into a method decorator
    """
    # 'obj' can be a class or a function. If 'obj' is a function at the time it
    # is passed to _dec,  it will eventually be a method of the class it is
    # defined on. If 'obj' is a class, the 'name' is required to be the name
    # of the method that will be decorated.
    def _dec(obj):
        is_class = isinstance(obj, type)
        if is_class:
            if name and hasattr(obj, name):
                func = getattr(obj, name)
                if not callable(func):
                    raise TypeError(
                        "Cannot decorate '{0}' as it isn't a callable "
                        "attribute of {1} ({2})".format(name, obj, func)
                    )
            else:
                raise ValueError(
                    "The keyword argument `name` must be the name of a method "
                    "of the decorated class: {0}. Got '{1}' instead".format(
                        obj, name,
                    )
                )
        else:
            func = obj

        def decorate(function):
            """
            Apply a list/tuple of decorators if decorator is one. Decorator
            functions are applied so that the call order is the same as the
            order in which they appear in the iterable.
            """
            if hasattr(decorator, '__iter__'):
                for dec in decorator[::-1]:
                    function = dec(function)
                return function
            return decorator(function)

        def _wrapper(self, *args, **kwargs):
            @decorate
            def bound_func(*args2, **kwargs2):
                return func.__get__(self, type(self))(*args2, **kwargs2)
            # bound_func has the signature that 'decorator' expects i.e.  no
            # 'self' argument, but it is a closure over self so it can call
            # 'func' correctly.
            return bound_func(*args, **kwargs)
        # In case 'decorator' adds attributes to the function it decorates, we
        # want to copy those. We don't have access to bound_func in this scope,
        # but we can cheat by using it on a dummy function.

        @decorate
        def dummy(*args, **kwargs):
            pass
        update_wrapper(_wrapper, dummy)
        # Need to preserve any existing attributes of 'func', including the name.
        update_wrapper(_wrapper, func)

        if is_class:
            setattr(obj, name, _wrapper)
            return obj

        return _wrapper
    # Don't worry about making _dec look similar to a list/tuple as it's rather
    # meaningless.
    if not hasattr(decorator, '__iter__'):
        update_wrapper(_dec, decorator)
    # Change the name to aid debugging.
    if hasattr(decorator, '__name__'):
        _dec.__name__ = 'method_decorator(%s)' % decorator.__name__
    else:
        _dec.__name__ = 'method_decorator(%s)' % decorator.__class__.__name__
    return _dec


def decorator_from_middleware_with_args(middleware_class):
    """
    Like decorator_from_middleware, but return a function
    that accepts the arguments to be passed to the middleware_class.
    Use like::

         cache_page = decorator_from_middleware_with_args(CacheMiddleware)
         # ...

         @cache_page(3600)
         def my_view(request):
             # ...
    """
    return make_middleware_decorator(middleware_class)


def decorator_from_middleware(middleware_class):
    """
    Given a middleware class (not an instance), return a view decorator. This
    lets you use middleware functionality on a per-view basis. The middleware
    is created with no params passed.
    """
    return make_middleware_decorator(middleware_class)()


# Unused, for backwards compatibility in Django 2.0.
def available_attrs(fn):
    """
    Return the list of functools-wrappable attributes on a callable.
    This was required as a workaround for http://bugs.python.org/issue3445
    under Python 2.
    """
    return WRAPPER_ASSIGNMENTS


def make_middleware_decorator(middleware_class):
    def _make_decorator(*m_args, **m_kwargs):
        middleware = middleware_class(*m_args, **m_kwargs)

        def _decorator(view_func):
            @wraps(view_func)
            def _wrapped_view(request, *args, **kwargs):
                if hasattr(middleware, 'process_request'):
                    result = middleware.process_request(request)
                    if result is not None:
                        return result
                if hasattr(middleware, 'process_view'):
                    result = middleware.process_view(request, view_func, args, kwargs)
                    if result is not None:
                        return result
                try:
                    response = view_func(request, *args, **kwargs)
                except Exception as e:
                    if hasattr(middleware, 'process_exception'):
                        result = middleware.process_exception(request, e)
                        if result is not None:
                            return result
                    raise
                if hasattr(response, 'render') and callable(response.render):
                    if hasattr(middleware, 'process_template_response'):
                        response = middleware.process_template_response(request, response)
                    # Defer running of process_response until after the template
                    # has been rendered:
                    if hasattr(middleware, 'process_response'):
                        def callback(response):
                            return middleware.process_response(request, response)
                        response.add_post_render_callback(callback)
                else:
                    if hasattr(middleware, 'process_response'):
                        return middleware.process_response(request, response)
                return response
            return _wrapped_view
        return _decorator
    return _make_decorator


class classproperty:
    def __init__(self, method=None):
        self.fget = method

    def __get__(self, instance, cls=None):
        return self.fget(cls)

    def getter(self, method):
        self.fget = method
        return self

5、从以上代码中,可以看到可以使用另外两个装饰器,分别decorator_from_middleware、decorator_from_middleware_with_args,分别使用如下:

from django.views import View
from django.utils.decorators import method_decorator, decorator_from_middleware
 
@decorator_from_middleware(LoginMiddleware2)
class AddUser(View):
    def get(self, request, id):
from django.views import View
from django.utils.decorators import method_decorator, decorator_from_middleware, decorator_from_middleware_with_args
 
@decorator_from_middleware_with_args(LoginMiddleware2)
class AddUser(View):
    def get(self, request, id):

接着还是报错,报错如下:AttributeError: 'function' object has no attribute 'as_view',表示这个注解不能用在as_view那里,于是乎我分别使用在底下的get方法前面,修改代码如下:

from django.views import View
from django.utils.decorators import method_decorator, decorator_from_middleware
 

class AddUser(View):
    @decorator_from_middleware(LoginMiddleware2)
    def get(self, request, id):

接着报错了:AttributeError: 'AddUser' object has no attribute 'session',说这个对象没有属性session,再试下另外一种咯:

from django.views import View
from django.utils.decorators import method_decorator, decorator_from_middleware, decorator_from_middleware_with_args
 

class AddUser(View):
    @decorator_from_middleware_with_args(LoginMiddleware2)
    def get(self, request, id):

发现还是报错:_decorator() got an unexpected keyword argument 'id',还是底层抛出来的异常。

6、接着,经过调试摸爬滚打后,终于搞出来了,AttributeError: 'AddUser' object has no attribute 'session',说这个对象没有属性session的时候发现了一个问题:

中间件的request底下才有个request,那个request才有session属性,所以,我修改一下中间件如下:

class LoginMiddleware2(MiddlewareMixin):
    """
    自定义权限校验中间件, 带多个参数时
    """
    def process_request(self, request):
        """
        :param request:
        :return:
        """
        if login_view.login_check(request.request) is not True:
            return login_view.login_check(request.request)

要调的时候加多一个request.即可,因为是中间件的request底下还有request,接着再调用即可,

from django.views import View
from django.utils.decorators import method_decorator, decorator_from_middleware
 

class AddUser(View):
    @decorator_from_middleware(LoginMiddleware2)
    def get(self, request, id):

7、以上内容就是我的中间件爬坑之路,大家可以慢慢看,以上内容仅供大家学习参考,谢谢!

猜你喜欢

转载自blog.csdn.net/u012561176/article/details/84869330