2009-05-10 15 views
36

Il metodo getattr() di Python è utile quando non si conosce in anticipo il nome di un determinato attributo.Esecuzione di una ricerca stile getattr() in un modello django

Questa funzionalità sarebbe anche utile nei modelli, ma non ho mai trovato un modo per farlo. Esiste un tag integrato o un tag non incorporato che può eseguire ricerche di attributi dinamici?

+0

Mi chiedo se stai provando a fare troppo nei tuoi modelli. getattr a volte si sente come la magia nera in codice Python quindi è sicuramente un odore di codice in un modello! –

risposta

53

Inoltre, come tag di un modello personalizzato, ho dovuto utilizzare anche lo write this code. Per gestire tutti gli scenari di ricerca, esegue prima una ricerca standard degli attributi, quindi cerca di cercare un dizionario, quindi prova una ricerca (per gli elenchi funzionanti), quindi segue il comportamento del modello Django standard quando un oggetto non trovato

(aggiornato 2009-08-26 per gestire ora le ricerche di indice lista pure) l'utilizzo

# 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:

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


+0

Brillante - grazie! – starsinmypockets

+0

Mi manca qualcosa qui - qual è lo scopo della seconda e della terza clausola? Se 'hasattr (valore, 'has_key')', allora non puoi accedervi in ​​un template usando 'value.arg'? Allo stesso modo se si tratta di un array, 'value.i' ottiene l'elemento i'th. Sono questi solo così la funzione gestisce questi casi ridondanti? – Symmetric

+1

Esiste la comodità di imitare il comportamento dei modelli Django: quando si esegue '{{valore.arg}}', Django controlla se è attributo dell'oggetto (clausola 1), una chiave di dizionario (clausola 2), un indice di lista (clausola 3), e quindi rientra in una stringa vuota per impostazione predefinita.Quindi sì, '{{value | getattribute: dynamic_arg_name}}' non è "getattribute" nel senso più puro di Python, ma si comporta allo stesso modo delle normali ricerche Django. – fotinakis

0

Non esiste un tag integrato, ma non dovrebbe essere troppo difficile per write your own.

2

Non penso. Ma non sarebbe troppo difficile scrivere un custom template tag per restituire un attributo nel dettato di contesto. Se si sta semplicemente cercando di restituire una stringa, provare qualcosa di simile:

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) 

Nota che probabilmente è altrettanto facile da fare questo nella vostra visualizzazione invece che nel modello, a meno che questa è una condizione che si ripete spesso nei tuoi dati.

2

Ho finito per aggiungere un metodo al modello in questione e questo metodo è accessibile come un attributo nel modello.

Tuttavia, penso che sarebbe bello se un tag integrato permettesse di cercare dinamicamente un attributo, poiché questo è un problema che molti di noi hanno costantemente nei nostri modelli.

2

Mantenere la distinzione tra ottenere e 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

Quel frammento mi ha salvato la giornata ma ne avevo bisogno per estenderlo alle relazioni, quindi l'ho modificato per dividere l'argomento con "." e ricorsivamente ottiene il valore. Potrebbe essere fatto in una riga: return getattribute(getattribute(value,str(arg).split(".")[0]),".".join(str(arg).split(".")[1:])) ma l'ho lasciato in 4 per la leggibilità. Spero che qualcuno abbia usato per questo.

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) 
Problemi correlati