[Python] Come visualizzare le eccezioni generate nella variabile del modello django?


Answers

Finalmente ho trovato una soluzione: ho sviluppato un tag di debug template:

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])

Quindi ora nel mio modello ho appena sostituito

{{ my_object.my_method }} by {% debug_var my_object.my_method %}
Question

All'interno di un modello Django, si può chiamare un metodo oggetto come questo:

{{ my_object.my_method }}

Il problema è quando ottieni un'eccezione / bug in "def my_method (self)", è nascosto quando si esegue il rendering del template (c'è invece un'uscita di stringa vuota, quindi non appare nessun errore).

Poiché desidero eseguire il debug di ciò che non va in "def my_method (self)", vorrei attivare qualcosa come una bandiera del django globale per ricevere tale eccezione.

in settings.py, ho già

DEBUG = True 
TEMPLATE_DEBUG = True

Posso ricevere molti tipi di eccezioni di template, ma nessuna quando seleziono un metodo object.

Cosa posso fare ?




TEMPLATE_STRING_IF_INVALID non funziona per me. Una soluzione rapida consiste nell'aprire env/lib64/python2.7/site-packages/django/template/base.py , trovare except Exception e lanciare una print e al suo interno (supponendo che tu stia usando manage.py runserver e possa vedere la stampa produzione).

Tuttavia, alcune righe in basso sono current = context.template.engine.string_if_invalid . Ho notato che string_if_invalid era vuoto nonostante avesse impostato TEMPLATE_STRING_IF_INVALID . Questo mi ha portato a questa parte dei documenti:

https://docs.djangoproject.com/en/1.8/ref/templates/upgrading/#the-templates-settings

Il sistema di template di Django è stato revisionato in Django 1.8 quando ha ottenuto il supporto per più template engine.

...

Se il tuo modulo delle impostazioni definisce ALLOWED_INCLUDE_ROOTS o TEMPLATE_STRING_IF_INVALID , includi i loro valori nelle chiavi ' allowed_include_roots ' e ' string_if_invalid ' nel dizionario ' OPTIONS '.

Quindi, oltre al trucco TemplateSyntaxError di @ 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")

è inoltre necessario definire string_if_invalid come segue

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,
            ...

Subito ho trovato un sacco di problemi che non avevo nemmeno saputo. Dovrebbe essere abilitato per impostazione predefinita. Per risolvere tag e filtri che si aspettano di fallire silenziosamente, ho lanciato dei condizionali intorno a loro:

{% if obj.might_not_exist %}
{{ obj.might_not_exist }}
{% endif %}

Anche se ho il sospetto che funzioni solo perché il {% if %} fallisce silenziosamente. Un altro approccio potrebbe essere quello di creare un filtro hasattr : {% if obj|hasattr:"might_not_exist" %} .




Simile alla risposta di S. Lott, attivare la shell di gestione (python manage.py shell) e creare l'istanza appropriata di my_object, chiamare my_method. O metti la gestione delle eccezioni in my_method e registra l'eccezione.