2009-05-10 7 views
36

Die Methode getattr() von Python ist nützlich, wenn Sie den Namen eines bestimmten Attributs nicht im Voraus kennen.Durchführen einer getattr() -Stilsuche in einer Django-Vorlage

Diese Funktionalität würde auch in Vorlagen nützlich sein, aber ich habe nie einen Weg gefunden, es zu tun. Gibt es ein integriertes Tag oder ein nicht integriertes Tag, das dynamische Attributsuchen durchführen kann?

+0

Ich frage mich, ob Sie versuchen, zu viel in Ihren Vorlagen zu tun. getattr fühlt sich manchmal wie schwarze Magie im Python-Code an, also ist es sicher ein Code-Geruch in einer Vorlage! –

Antwort

53

Ich hatte auch write this code als benutzerdefinierte Vorlage Tag vor kurzem. Um alle Nachschlagszenarien zu behandeln, führt es zunächst eine Standardattributsuche durch, versucht dann, eine Wörterbuchsuche durchzuführen, und versucht dann, eine getitem Suche (für Listen zu arbeiten), folgt dann Standard-Django-Vorlagenverhalten, wenn ein Objekt wird nicht gefunden.

# app/templatetags/getattribute.py 

import re 
from django import template 
from django.conf import settings 

numeric_test = re.compile("^\d+$") 
register = template.Library() 

def getattribute(value, arg): 
    """Gets an attribute of an object dynamically from a string name""" 

    if hasattr(value, str(arg)): 
     return getattr(value, arg) 
    elif hasattr(value, 'has_key') and value.has_key(arg): 
     return value[arg] 
    elif numeric_test.match(str(arg)) and len(value) > int(arg): 
     return value[int(arg)] 
    else: 
     return settings.TEMPLATE_STRING_IF_INVALID 

register.filter('getattribute', getattribute) 

Template Nutzung (aktualisiert 2009-08-26 jetzt Liste Indexsuchen als auch handhaben):

{% load getattribute %} 
{{ object|getattribute:dynamic_string_var }} 


+0

Brilliant - danke! – starsinmypockets

+0

Ich vermisse etwas hier - was ist der Zweck der zweiten und dritten Klauseln? Wenn 'hasattr (Wert, 'has_key')' ', kann man dann nicht in einer Vorlage mit' value.arg' darauf zugreifen? Ähnlich, wenn es ein Array ist, erhält 'value.i' das i-te Element. Sind diese nur so, dass die Funktion diese redundanten Fälle behandelt? – Symmetric

+1

Diese gibt es als Bequemlichkeit, um das Verhalten von Django-Templates zu imitieren - wenn Sie '{{value.arg}}' machen, überprüft Django, ob es ein Attribut des Objekts (Klausel 1), ein Dictionary-Schlüssel (Klausel 2), ein Listenindex ist (Klausel 3) und fällt dann standardmäßig auf eine leere Zeichenfolge.Also ja, '{{value | getattribute: dynamic_arg_name}}' ist nicht "getattribute" im reinsten Python-Sinne, aber es verhält sich genauso wie normale Django-Lookups. – fotinakis

2

Ich glaube nicht. Aber es wäre nicht schwer, eine custom template tag zu schreiben, um ein Attribut im Kontext dict zurückzugeben. Wenn Sie einfach nur du versucht, eine Zeichenfolge zurück, versuchen, etwas wie folgt aus:

class GetAttrNode(template.Node): 
    def __init__(self, attr_name): 
     self.attr_name = attr_name 

    def render(self, context): 
     try: 
      return context[self.attr_name] 
     except: 
      # (better yet, return an exception here) 
      return '' 

@register.tag 
def get_attr(parser, token): 
    return GetAttrNode(token) 

Hinweis, dass es wahrscheinlich genauso einfach dies Ihrer Meinung nach tun, anstatt in der Vorlage, es sei denn, dies ist eine Bedingung, die wiederholt oft in deinen Daten.

2

Ich habe am Ende eine Methode zu dem fraglichen Modell hinzugefügt, und auf diese Methode kann wie ein Attribut in der Vorlage zugegriffen werden.

Noch, ich denke, es wäre toll, wenn ein eingebauter Tag Ihnen erlaubt, dynamisch ein Attribut zu suchen, da dies ein Problem ist, das viele von uns ständig in unseren Vorlagen haben.

2

halten die Unterscheidung zwischen bekommen und getattr,

@register.filter(name='get') 
def get(o, index): 
    try: 
     return o[index] 
    except: 
     return settings.TEMPLATE_STRING_IF_INVALID 


@register.filter(name='getattr') 
def getattrfilter(o, attr): 
    try: 
     return getattr(o, attr) 
    except: 
     return settings.TEMPLATE_STRING_IF_INVALID 
0

Dieser Ausschnitt rettete meinen Tag, aber ich brauchte ihn, um ihn über Beziehungen zu verteilen, also änderte ich ihn, um den Arg durch "." und rekursiv den Wert erhalten. Es könnte in einer Zeile gemacht werden: return getattribute(getattribute(value,str(arg).split(".")[0]),".".join(str(arg).split(".")[1:])) aber ich verließ es in 4 für die Lesbarkeit. Ich hoffe, jemand hat dafür Gebrauch gemacht.

import re 
from django import template 
from django.conf import settings 

numeric_test = re.compile("^\d+$") 
register = template.Library() 

def getattribute(value, arg): 
"""Gets an attribute of an object dynamically AND recursively from a string name""" 
    if "." in str(arg): 
     firstarg = str(arg).split(".")[0] 
     value = getattribute(value,firstarg) 
     arg = ".".join(str(arg).split(".")[1:]) 
     return getattribute(value,arg) 
    if hasattr(value, str(arg)): 
     return getattr(value, arg) 
    elif hasattr(value, 'has_key') and value.has_key(arg): 
     return value[arg] 
    elif numeric_test.match(str(arg)) and len(value) > int(arg): 
     return value[int(arg)] 
    else: 
     #return settings.TEMPLATE_STRING_IF_INVALID 
     return 'no attr.' + str(arg) + 'for:' + str(value) 

register.filter('getattribute', getattribute) 
Verwandte Themen