КОНТЕКСТ:
Я работаю над приложением 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, ни один из слоев лука
внутри этого слоя (включая представление) увидим запрос или
ответ. Ответ будет возвращаться только через те же слои, которые
запрос прошел до конца.