2009-09-29 6 views
14

J'ai un filtre currency, qui prend une valeur en USD et le convertit en une devise (USD ou GBP). La devise à convertir est stockée dans la session, mais les filtres ne prennent pas RequestContext, donc je ne peux pas l'attraper directement à partir de là.Django - accéder au RequestContext à partir d'un filtre personnalisé

Existe-t-il un meilleur moyen que de passer l'élément de session pertinent dans le modèle, et du modèle dans le filtre en tant qu'argument? Bien que cette approche fonctionne, elle semble assez horrible, et je vais probablement passer la monnaie à (presque) tous les modèles.

Mon filtre ressemble actuellement quelque chose comme ceci:

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) 

Répondre

7

Si vous créez une étiquette de modèle au lieu d'un filtre, vous avez le contexte de travailler avec (qui contient la demande). http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-tags

+1

@Adam - peut-être que je suis épais, mais je ne vois pas dans ces documents comment accéder aux variables de session à l'intérieur des balises de gabarit personnalisées. Pourriez-vous me donner un exemple ou signaler ce qui me manque? –

+1

Ah - creuser autour semble être passé comme le premier argument à l'étiquette (juste trouvé un exemple qui ajoute une classe aux liens correspondant à une expression régulière, qui semble (a) utile et (b) adaptable - http: // gnuvince .wordpress.com/2007/09/14/a-django-template-tag-pour-la-page-courante-active /). –

+0

À droite, le contexte est un paramètre standard de la méthode render sur le nœud de l'étiquette du modèle. Si vous avez besoin d'un autre exemple spécifique, je suis sûr que je pourrais en rassembler un. – Adam

3

Ceci peut être fait en utilisant un filtre. D'abord assurez-vous que vous avez "django.core.context_processors.request" en vous TEMPLATE_CONTEXT_PROCESSORS. Si vous ne le faites pas, vous pouvez ajouter à votre fichier settings.py:

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

Ensuite, dans votre modèle, votre filtre ressemblera à ceci (en supposant que votre variable de session est nommé « currency_type »):

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

Ou est quelque chose comme ça que vous considérez assez horrible?

+0

Il est; Regardez ce qu'il fait, ce filtre décrit exactement ce que vous dites. – fijter

+0

Je pensais qu'il pourrait se plaindre du processus d'avoir à passer explicitement la variable à travers la vue. Cela le rendra disponible automatiquement. Je peux aussi poster un exemple d'utilisation d'un tag template avec takes_context = True dans le décorateur – Zach

3

Je serais d'accord avec Adam que la migration du code vers un tag personnalisé est la meilleure façon.

Toutefois, un client avait besoin d'enregistrer l'utilisation de certains filtres uniquement lorsqu'une page était publiée et avait un vaste inventaire de modèles qui utilisaient la syntaxe de filtre existante. Il aurait été coûteux de réécrire tous les modèles. Alors, je suis venu avec cette fonction simple qui extrait le contexte de la pile d'appel:

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 

Assurez-vous de lire mes avertissements, mais cela ne donne filtres standard un accès au contexte (lorsqu'elle est disponible) SANS avoir à transformer votre filtre en une étiquette.

+1

C'est génial, pas sûr si c'est génial, mais génial;) –

0

Une solution un peu moins hacky à la proposition de Daniel Rhoden est, d'utiliser threading.local(). Définissez une classe de middleware, qui stocke votre request en tant qu'objet global dans votre thread local et ajoutez cette classe à votre MIDDLEWARE_CLASSES.

Maintenant, un filtre de modèle peut facilement accéder à cet objet de requête.

Questions connexes