Dominic Rodger
Dominic Rodger

Reputation: 99771

Django - accessing the RequestContext from within a custom filter

I've got a filter currency, which takes a value in USD and converts it to a currency (either USD or GBP). The currency to convert to is stored in the session, but filters don't take RequestContext, so I can't grab it straight from there.

Is there a better way than passing the relevant session element into the template, and from the template into the filter as an argument? Whilst this approach is working, it seems fairly horrible, and I'm likely to end up passing the currency to (almost) every template.

My filter currently looks something like this:

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)

Upvotes: 21

Views: 16312

Answers (4)

jrief
jrief

Reputation: 1695

A somehow less hacky solution to Daniel Rhoden's proposal is, to use threading.local(). Define a middleware class, which stores your request as a global object inside your local thread, and add that class to your MIDDLEWARE_CLASSES.

Now a template filter can easily access that request object.

Upvotes: 1

Daniel Rhoden
Daniel Rhoden

Reputation: 6127

I would have to agree with Adam that migrating the code to a custom tag is the best way.

However, a client needed to record the use of certain filters only when a page was published and had a HUGE inventory of templates that used the existing filter syntax. It would have been a costly undertaking to rewrite all the templates. So, I came up with this simple function that extracts the context from the call stack:

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

Be sure to read my warnings, but this DOES give standard filters access to the context (when it is available) WITHOUT having to turn your filter into a tag.

Upvotes: 8

Zach
Zach

Reputation: 19082

This can be done using a filter. First make sure that you have "django.core.context_processors.request" in you TEMPLATE_CONTEXT_PROCESSORS. If you don't, you can add this to your settings.py file:

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

Then in your template, your filter will look like this (assuming your session variable is named 'currency_type'):

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

Or is something like this what you are considering fairly horrible?

Upvotes: 5

Adam
Adam

Reputation: 7207

If you create a template tag instead of a filter, you are given the context to work with (which contains the request). http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-tags

Upvotes: 14

Related Questions