python - para - plantillas django
¿Cómo ver la excepción generada en la variable de plantilla django? (6)
Dentro de una plantilla de Django, uno puede llamar a un método de objeto como este:
{{ my_object.my_method }}
El problema es cuando obtienes una excepción / error en ''def my_method (self)'', se oculta al procesar la plantilla (en su lugar, hay un resultado de cadena vacía, por lo que no aparece ningún error).
Como quiero depurar lo que está mal en ''def my_method (self)'', me gustaría activar algo así como una bandera global django para recibir esa excepción.
en settings.py, ya tengo
DEBUG = True
TEMPLATE_DEBUG = True
Puedo recibir muchos tipos de excepciones de plantilla, pero ninguna cuando trigloco un método de objeto.
Que puedo hacer ?
Que puedo hacer ?
Evalúa el método de generación de excepciones en tu función de vista.
def someView( request ):
.... all the normal work ...
my_object.my_method() # Just here for debugging.
return render_to_response( ... all the normal stuff... )
Puede eliminar esa línea de código cuando termine de depurar.
De forma similar a la respuesta de S. Lott, active el shell de gestión (shell python manage.py) y cree la instancia apropiada de my_object, llame a my_method. O ponga el manejo de excepciones en my_method y registre la excepción.
Usaría pruebas unitarias para aislar el problema. Sé que esta es una respuesta indirecta, pero creo que esta es la forma ideal de resolver y evitar que el problema vuelva.
Finalmente encontré una solución: desarrollé una etiqueta de depuración de plantilla:
from django import template
import traceback
class DebugVariable(template.Variable):
def _resolve_lookup(self, context):
current = context
for bit in self.lookups:
try: # dictionary lookup
current = current[bit]
except (TypeError, AttributeError, KeyError):
try: # attribute lookup
current = getattr(current, bit)
if callable(current):
if getattr(current, ''alters_data'', False):
current = settings.TEMPLATE_STRING_IF_INVALID
else:
try: # method call (assuming no args required)
current = current()
except:
raise Exception("Template Object Method Error : %s" % traceback.format_exc())
except (TypeError, AttributeError):
try: # list-index lookup
current = current[int(bit)]
except (IndexError, # list index out of range
ValueError, # invalid literal for int()
KeyError, # current is a dict without `int(bit)` key
TypeError, # unsubscriptable object
):
raise template.VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute
except Exception, e:
if getattr(e, ''silent_variable_failure'', False):
current = settings.TEMPLATE_STRING_IF_INVALID
else:
raise
except Exception, e:
if getattr(e, ''silent_variable_failure'', False):
current = settings.TEMPLATE_STRING_IF_INVALID
else:
raise
return current
class DebugVarNode(template.Node):
def __init__(self, var):
self.var = DebugVariable(var)
def render(self, context):
return self.var.resolve(context)
@register.tag(''debug_var'')
def do_debug_var(parser, token):
"""
raise every variable rendering exception, TypeError included (usually hidden by django)
Syntax::
{% debug_var obj.my_method %} instead of {{ obj.my_method }}
"""
bits = token.contents.split()
if len(bits) != 2:
raise template.TemplateSyntaxError("''%s'' tag takes one argument" % bits[0])
return DebugVarNode(bits[1])
Entonces ahora en mi plantilla simplemente reemplazo
{{ my_object.my_method }} by {% debug_var my_object.my_method %}
Aquí hay un buen truco que acabo de implementar para hacer exactamente esto. Pon esto en tu configuración de depuración:
class InvalidString(str):
def __mod__(self, other):
from django.template.base import TemplateSyntaxError
raise TemplateSyntaxError(
"Undefined variable or unknown value for: %s" % other)
TEMPLATE_STRING_IF_INVALID = InvalidString("%s")
Esto causará que se genere una TemplateSyntaxError cuando el análisis ve un valor desconocido o no válido. He probado esto un poco (con nombres de variable indefinidos) y funciona muy bien. No he probado con valores de retorno de función, etc. Las cosas podrían complicarse.
TEMPLATE_STRING_IF_INVALID
no funciona para mí. Una solución rápida es abrir env/lib64/python2.7/site-packages/django/template/base.py
, buscar except Exception
y lanzar una print e
dentro de él (suponiendo que está utilizando manage.py runserver
y puede ver imprimir salida).
Sin embargo, algunas líneas abajo son current = context.template.engine.string_if_invalid
. Noté que string_if_invalid
estaba vacío a pesar de haber configurado TEMPLATE_STRING_IF_INVALID
. Esto me lleva a esta parte de los documentos:
https://docs.djangoproject.com/en/1.8/ref/templates/upgrading/#the-templates-settings
El sistema de plantillas de Django fue revisado en Django 1.8 cuando obtuvo soporte para múltiples motores de plantillas.
...
Si su módulo de configuración define
ALLOWED_INCLUDE_ROOTS
oTEMPLATE_STRING_IF_INVALID
, incluya sus valores bajo lasallowed_include_roots
''allowed_include_roots
'' y ''string_if_invalid
'' en el diccionario ''OPTIONS
''.
Entonces, además del truco TemplateSyntaxError de @ slacy ,
class InvalidString(str): def __mod__(self, other): from django.template.base import TemplateSyntaxError raise TemplateSyntaxError( "Undefined variable or unknown value for: %s" % other) TEMPLATE_STRING_IF_INVALID = InvalidString("%s")
también necesita definir string_if_invalid
siguiente manera
TEMPLATES = [
{
''BACKEND'': ''django.template.backends.django.DjangoTemplates'',
''DIRS'': [os.path.join(BASE_DIR, ''templates'')],
''APP_DIRS'': True,
''OPTIONS'': {
''string_if_invalid'': TEMPLATE_STRING_IF_INVALID,
...
Directamente, esto encontró un montón de problemas que tenía que ni siquiera sabía. Realmente debería estar habilitado por defecto. Para resolver etiquetas y filtros que esperan fallar silenciosamente, lancé condicionales a su alrededor:
{% if obj.might_not_exist %}
{{ obj.might_not_exist }}
{% endif %}
Aunque sospecho que esto solo funciona porque el {% if %}
falla silenciosamente. Otro enfoque podría ser crear un filtro hasattr
: {% if obj|hasattr:"might_not_exist" %}
.