Какие типовые задачи решаются через middleware django?
Изучаю middleware, написал подсчет визитов через middleware, но меня мучает вопрос - Какие типовые задачи решаются с помощью промежуточного слоя? Я могу предположить, что это различные действия над аргументами request и тд, пока не сталкивался с какой-то проблемой, которую нужно решать конкретно через middleware. Если можете, опишите проблемы, которые вы решали через этот механизм.
1. Идентификация пользователя. Например, кастомная аутентификация или навешивание каких-нибудь перманентных куков
2. Сессии
3. Логгирование ответов/запросов
4. Ограничение доступа к множеству URL'ов с определённым префиксом
5. Инъекции в HTML-ответ
1. Зачем навешивать перманентные куки?
2. Механизм сессий в джанге неплохой, какие кастомные задачи можно перенести в мидлварь оттуда?
3. А вот это мне нравится - да, только вот про запросы я понял, а зачем логировать ответы? + это же должен делать nginx, разве нет?
5. А почему не сделать это из view? Или имеется в виду сделать инъекцию в абсолютно все страницы?
xxx, я отвечал на вопрос "какие задачи решаются с помощью middleware", а не на вопрос "какие задачи решаются только с помощью middleware".
Если тебе не нужны перманентные куки — у тебя просто ещё не было подобных задач.
Механизм сессий в джанге обычный. Например, нельзя инвалидировать все сессии конкретного юзера за время, меньшее чем O(n) (перебор всех сессий на сервере).
А уж если у тебя приложение на другом каком-то фреймворке и вообще не на питоне — то весь механизм сессий уж точно придётся переписывать.
nginx может логгировать, конечно. Но ты можешь захотеть логгировать не сырые данные, а, например, все запросы юзеров, ID которых кратно 42 (ну мало ли). Средствами nginx, наверное, будет непросто это сделать, не так ли?
А почему не сделать это из view? Или имеется в виду сделать инъекцию в абсолютно все страницы?
Посмотри на прекрасный модуль django-debug-toolbar и то, как он показывает этот самый "тулбар" на всех страницах и сам себе ответишь на вопрос.
А вот ещё я вспомнил из реального опыта удачный пример милдваря. К каждому request вяжется объект корзины, с которым очень удобно работать во вьюхах. Без мидлваря это были бы какие-то функции-хэлперы. Сами корзины хранятся в сессиях, которые хранятся прямо в куках пользователей (signed cookie session). Таким образом на сервере тратится ровно 0 байт для хранения пользовательских сессий и корзин.
from django.utils.deprecation import MiddlewareMixin
from .basket import Basket
class BasketMiddleware(MiddlewareMixin):
def process_request(self, request):
request.basket = Basket(request.session.get('basket', {}))
def process_response(self, request, response):
if getattr(request, 'basket', None) is None:
return response
if request.basket.changed:
request.session['basket'] = request.basket.to_dict()
request.session.save()
return response
# basket.py
from collections import UserDict
from core.models import ProductOption, Product
from .models import Item
class Basket(UserDict):
changed = False
def add(self, quantity=0, option=None, set_=False):
self.changed = True
id_ = str(option.product.id)
option = str(option.id)
self.setdefault(id_, {})
self[id_].setdefault(option, 0)
if set_:
self[id_][option] = quantity
else:
self[id_][option] += quantity
if self[id_][option] <= 0:
del self[id_][option]
if not self[id_]:
del self[id_]
return 0
else:
return self[id_][option]
@property
def total_count(self):
return sum(x for product, options in self.items() for _, x in options.items())
@property
def total_price(self):
prices = {str(id_): price for id_, price in
Product.objects.filter(id__in=self.keys()).values_list('id', 'price')}
return sum(x * prices[product] for product, options in self.items() for _, x in options.items())
def cost(self, option):
price = option.product.price
return self.count_option(option) * price
def count_option(self, option):
product_id = str(option.product.id)
option_id = str(option.id)
return self.get(product_id, {}).get(option_id, 0)
def flush(self):
self.changed = True
for key in list(self):
del self[key]
def build_order(self, order):
items = []
for product_id, data in self.items():
product = Product.objects.get(id=product_id)
for option_id, quantity in data.items():
if quantity == 0:
continue
option = None
if option_id != '0':
option = ProductOption.objects.get(id=option_id)
items.append(
Item(order=order, option=option, quantity=quantity, product=product)
)
order.items.bulk_create(items)
self.flush()
return order
def fix(self):
ids = self.keys()
exist_in_db = (Product.objects
.filter(id__in=ids, options__in_stock=True, options__show=True)
.values_list('id', flat=True))
to_remove = set(ids) - set(str(x) for x in exist_in_db)
for id_ in to_remove:
del self[id_]
if to_remove:
self.changed = True
def to_dict(self):
return dict(self)
И да, код писался давно, проект давно не поддерживается, есть очевидные N+1 проблемы, но оптимизировать их не имеет смысла. Говорить мне об этом не нужно.