Django - доступ к RequestContext из настраиваемого фильтра

У меня есть фильтр currency, который принимает значение в долларах США и конвертирует его в валюту (USD или GBP). Валюта для конвертирования в хранится в сеансе, но фильтры не принимают RequestContext, поэтому я не могу взять ее прямо оттуда.

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

В настоящее время мой фильтр выглядит примерно так:

def currency(value, currency):
    if currency == 'usd':
       val = '$%.2f' % value
       return mark_safe(val)

    d = Decimal(value)
    val = '£%.2f' % (d*Decimal('0.63'))

    return mark_safe(val)

Ответы

Ответ 2

Это можно сделать с помощью фильтра. Сначала убедитесь, что у вас "django.core.context_processors.request" у вас TEMPLATE_CONTEXT_PROCESSORS. Если вы этого не сделаете, вы можете добавить это в свой файл settings.py:

TEMPLATE_CONTEXT_PROCESSORS += (
    "django.core.context_processors.request"
)

Затем в вашем шаблоне ваш фильтр будет выглядеть так (если ваша переменная сеанса называется "currency_type" ):

{{value|currency:request.session.currency_type}}

Или что-то вроде этого, что вы считаете довольно ужасным?

Ответ 3

Я бы согласился с Адамом, что наилучшим способом является перенос кода на пользовательский тег.

Тем не менее, клиенту необходимо было зарегистрировать использование определенных фильтров только в том случае, если страница была опубликована, и у нее был огромный каталог шаблонов, в которых использовался существующий синтаксис фильтра. Было бы дорогостоящим обязательством переписать все шаблоны. Итак, я придумал эту простую функцию, которая извлекает контекст из стека вызовов:

https://gist.github.com/drhoden/e05292e52fd5fc92cc3b

def get_context(max_depth=4):
    import inspect
    stack = inspect.stack()[2:max_depth]
    context = {}
    for frame_info in stack:
        frame = frame_info[0]
        arg_info = inspect.getargvalues(frame)
        if 'context' in arg_info.locals:
            context = arg_info.locals['context']
            break
    return context

Обязательно прочтите мои предупреждения, но это дает стандартным фильтрам доступ к контексту (когда он доступен) БЕЗ необходимости превратить ваш фильтр в тег.

Ответ 4

Как-то менее хакерское решение для предложения Даниэля Родена - использовать threading.local(). Определите класс промежуточного программного обеспечения, в котором ваш request будет храниться как глобальный объект внутри вашего локального потока и добавьте этот класс в ваш MIDDLEWARE_CLASSES.

Теперь фильтр шаблонов может легко получить доступ к этому объекту запроса.