diff --git a/django/core/exceptions.py b/django/core/exceptions.py index 9d1aab665f..297262f9b6 100644 --- a/django/core/exceptions.py +++ b/django/core/exceptions.py @@ -1,13 +1,11 @@ "Global Django exceptions" -from django.core.template import SilentVariableFailure - class Http404(Exception): pass -class ObjectDoesNotExist(SilentVariableFailure): +class ObjectDoesNotExist(Exception): "The requested object does not exist" - pass + silent_variable_failure = True class SuspiciousOperation(Exception): "The user did something suspicious" diff --git a/django/core/template/__init__.py b/django/core/template/__init__.py index e498fda00c..713a082735 100644 --- a/django/core/template/__init__.py +++ b/django/core/template/__init__.py @@ -102,10 +102,6 @@ class TemplateDoesNotExist(Exception): class VariableDoesNotExist(Exception): pass -class SilentVariableFailure(Exception): - "Any function raising this exception will be ignored by resolve_variable" - pass - class InvalidTemplateLibrary(Exception): pass @@ -662,12 +658,15 @@ def resolve_variable(path, context): else: try: # method call (assuming no args required) current = current() - except SilentVariableFailure: - current = '' except TypeError: # arguments *were* required # GOTCHA: This will also catch any TypeError # raised in the function itself. current = '' # invalid method call + except Exception, e: + if getattr(e, 'silent_variable_failure', False): + current = '' + else: + raise except (TypeError, AttributeError): try: # list-index lookup current = current[int(bits[0])] diff --git a/docs/templates_python.txt b/docs/templates_python.txt index de212cd141..3bedf67caa 100644 --- a/docs/templates_python.txt +++ b/docs/templates_python.txt @@ -147,10 +147,10 @@ Method lookups are slightly more complex than the other lookup types. Here are some things to keep in mind: * If, during the method lookup, a method raises an exception, the exception - will be propagated, unless the exception subclasses - ``django.core.template.SilentVariableFailure``. If the exception - subclasses ``SilentVariableFailure``, the variable will render as an - empty string. Example:: + will be propagated, unless the exception has an attribute + ``silent_variable_failure`` whose value is ``True``. If the exception + *does* have a ``silent_variable_failure`` attribute, the variable will + render as an empty string. Example:: >>> t = Template("My name is {{ person.first_name }}.") >>> class PersonClass3: @@ -162,15 +162,21 @@ some things to keep in mind: ... AssertionError: foo - >>> from django.core.template import SilentVariableFailure - >>> class SilentAssertionError(SilentVariableFailure): pass + >>> class SilentAssertionError(Exception): + ... silent_variable_failure = True >>> class PersonClass4: ... def first_name(self): - ... raise SilentAssertionError, "foo" + ... raise SilentAssertionError >>> p = PersonClass4() >>> t.render(Context({"person": p})) "My name is ." + Note that ``django.core.exceptions.ObjectDoesNotExist``, which is the + base class for all Django database API ``DoesNotExist`` exceptions, has + ``silent_variable_failure = True``. So if you're using Django templates + with Django model objects, any ``DoesNotExist`` exception will fail + silently. + * A method call will only work if the method has no required arguments. Otherwise, the system will move to the next lookup type (list-index lookup). diff --git a/tests/othertests/templates.py b/tests/othertests/templates.py index a92fc5ef00..a95edf6bdc 100644 --- a/tests/othertests/templates.py +++ b/tests/othertests/templates.py @@ -32,6 +32,12 @@ template.libraries['django.templatetags.testtags'] = register # Helper objects for template tests # ##################################### +class SomeException(Exception): + silent_variable_failure = True + +class SomeOtherException(Exception): + pass + class SomeClass: def __init__(self): self.otherclass = OtherClass() @@ -42,6 +48,12 @@ class SomeClass: def method2(self, o): return o + def method3(self): + raise SomeException + + def method4(self): + raise SomeOtherException + class OtherClass: def method(self): return "OtherClass.method" @@ -133,7 +145,14 @@ TEMPLATE_TESTS = { 'basic-syntax31': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'), # Default argument testing - 'basic-syntax32' : (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'), + 'basic-syntax32': (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'), + + # Fail silently for methods that raise an exception with a "silent_variable_failure" attribute + 'basic-syntax33': (r'1{{ var.method3 }}2', {"var": SomeClass()}, "12"), + + # In methods that raise an exception without a "silent_variable_attribute" set to True, + # the exception propogates + 'basic-syntax34': (r'1{{ var.method4 }}2', {"var": SomeClass()}, SomeOtherException), ### IF TAG ################################################################ 'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),