Как правильно подключить промежуточное ПО в Django? - PullRequest
2 голосов
/ 15 мая 2019

КОНТЕКСТ:

Я работаю над приложением Django 1.10 / Python2.7 с устаревшими компонентами и готовлю часть к нему, чтобы в ближайшее время выплатить часть технического долга. Для этого мне нужно поместить некоторую логику в цепочку промежуточного программного обеспечения, используемого в приложении, чтобы обойти весь набор слоев под слоями Django, если запрашиваемый URL-адрес должен попасть в новое приложение API (/api маршруты).

Моя идея состояла в том, чтобы представить новое промежуточное программное обеспечение между Django и пользовательским проектом (в качестве примера того, что есть у proj - прокомментировано ниже как «Пользовательское промежуточное программное обеспечение») - всего около 8 промежуточных программ, некоторые из которых делают десятки обращений к БД, и я пока не знаю последствий их удаления или превращения в декораторы для запросов / представлений, которые в них нуждаются).

Промежуточное промежуточное программное обеспечение замкнет все те, что под ним, на MIDDLEWARE, если URL начинается с /api.

Я пытался замкнуть цепь Middleware в Django, как говорится в документации, но у меня это не работает.

КАК ЭТО РАБОТАЕТ (но не идеально):

То, как я понял, работало так: (это не то, что говорится в документации):

Это уже существующая цепочка MIDDLEWARE в settings.py в проекте:

(...)
MIDDLEWARE = (
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',

    'app2.middleware.PreExisting1Middleware',                   # Custom middlewares
    'app3.middleware.PreExisting2Middleware',                   # Custom middlewares
)
(...)

Я добавил новый settings.py ключ с:

SHORTCIRCUIT_MIDDLWARES_IF_URL_PATTERNS_IN = (r'^/api', )

Чтобы не применять PreExisting1Middleware, PreExisting2Middleware для маршрутов URL, начинающихся с /api, я создал базовый класс Middleware следующим образом:

import re

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.deprecation import MiddlewareMixin


class ShortCircuitMiddlewareMixin(MiddlewareMixin):
    def __call__(self, request):
        # Code to be executed for each request before the view (and later middleware) are called.
        response = None
        if hasattr(self, 'process_request') and self.should_middleware_be_applied(request):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response') and self.should_middleware_be_applied(request):
            response = self.process_response(request, response)
        return response

    def should_middleware_be_applied(self, request):
        short_patterns = getattr(settings, 'SHORTCIRCUIT_MIDDLWARES_IF_URL_PATTERNS_IN', [])
        if hasattr(short_patterns, '__iter__'):
            if any(re.match(pattern, request.path) for pattern in short_patterns):
                print('\nShort-circuiting the middleware: {}'.format(self.__class__.__name__))
                return False
        else:
            raise ImproperlyConfigured(
                "SHORTCIRCUIT_MIDDLWARES_IF_URL_PATTERNS_IN must be an iterable, got '{}'".format(short_patterns))
        return True

Затем я использую его в качестве базового класса для промежуточного программного обеспечения PreExisting1Middleware и PreExisting2Middleware, таким образом, они замыкаются накоротко, если упомянутое условие url выполняется.

Class PreExisting1Middleware(ShortCircuitMiddlewareMixin):
    def process_request(self, request):
        print('\nprocessing...')
        (...)

Class PreExisting2Middleware(ShortCircuitMiddlewareMixin):
    def process_request(self, request):
        (...)

Это прекрасно работает, и я получаю хорошее сообщение о оболочке, в котором говорится, какие промежуточные программы были обойдены, и которые можно / нужно регистрировать при необходимости.

Теперь перейдем к вопросу ....

МОЙ ВОПРОС:

Есть идеи, как это сделать, используя правильную логику в методе __call__ ниже и в конфигурации MIDDLEWARE ниже?

class ConditionallyShortcircuitChainMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response
(...)
MIDDLEWARE = (
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',

    'app1.middleware.ConditionallyShortcircuitChainMiddleware', # My new middleware
    'app2.middleware.PreExisting1Middleware',                   # Custom
    'app3.middleware.PreExisting2Middleware',                   # Custom
)
(...)

Соответствующая информация из документов: См. здесь

Заказ промежуточного программного обеспечения и расслоение

На этапе запроса, перед вызовом представления, применяется Django промежуточное ПО в порядке, указанном в MIDDLEWARE, сверху вниз.

Вы можете думать об этом как о луке: каждый класс промежуточного программного обеспечения является «слоем» это оборачивает взгляд, который находится в ядре лука. Если запрос проходит через все слои лука (каждый вызывает get_response для передачи запроса на следующий уровень), вплоть до вид в ядре, ответ будет проходить через каждый слой (в обратном порядке) на обратном пути.

Если один из слоев решает замкнуть накоротко и вернуть ответ не вызывая его get_response, ни один из слоев лука внутри этого слоя (включая представление) увидим запрос или ответ. Ответ будет возвращаться только через те же слои, которые запрос прошел до конца.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...