Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions admin_interface/templates/admin/base_site.html
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@
{% if theme.list_filter_highlight %} list-filter-highlight {% endif %}
{% if theme.list_filter_sticky %} list-filter-sticky {% endif %}

{% admin_interface_resolve_variable "adminform" as adminform %}
{% admin_interface_resolve_variable "inline_admin_formsets" as inline_admin_formsets %}

{% if adminform and inline_admin_formsets %}
{% admin_interface_use_changeform_tabs adminform inline_admin_formsets as admin_interface_use_changeform_tabs %}
{% if admin_interface_use_changeform_tabs %}
Expand Down
17 changes: 17 additions & 0 deletions admin_interface/templatetags/admin_interface_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,23 @@ def admin_interface_use_changeform_tabs(adminform, inline_forms):
return has_tabs


@register.simple_tag(takes_context=True)
def admin_interface_resolve_variable(context, var_name, default=""):
bits = var_name.split(".")
current = context.flatten() # merge all and create a context dict

for bit in bits:
try:
if isinstance(current, dict):
current = current[bit]
else:
current = getattr(current, bit)
except (KeyError, AttributeError, TypeError):
return default
Comment on lines +193 to +203
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

template.Variable(var_name).resolve(context) already do all the resolution with dot syntax, so the code before is not necessary.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback!
You’re right that template.Variable(var_name).resolve(context) already handles dotted lookups. Reason I added the pre-check is because Variable.resolve() logs a VariableDoesNotExist exception for every missing lookup,

From Django’s source (django/template/base.py, around lines 913 and 939), you can see the logger call:

except Exception as e:
    template_name = getattr(context, "template_name", None) or "unknown"
    logger.debug(
        "Exception while resolving variable '%s' in template '%s'.",
        bit,
        template_name,
        exc_info=True,
    )

So if we just wrap Variable.resolve() in a try/except, we still get log noise:


One possible workaround is to temporarily silence the django.template logger while resolving, and then restore its previous state:

    logger = logging.getLogger("django.template")
    old_level = logger.level
    try:
        logger.setLevel(logging.INFO)
        return template.Variable(var_name).resolve(context)
    except VariableDoesNotExist:
        return default
    finally:
        logger.setLevel(old_level)

This approach does work, but it temporarily changes the global logger state.
At the moment I haven’t found an alternative solution
Please let me know if there’s a cleaner hook in Django’s template system to avoid this log pollution.
Or else we can proceed with this changes to the logging itself temporarily.


return template.Variable(var_name).resolve(context)


@register.filter
def admin_interface_slugify(name):
return slugify(str(name or ""))
97 changes: 97 additions & 0 deletions tests/test_resolve_variable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from django.template import Context, Template
from django.test import SimpleTestCase


class ResolveVariableTagTests(SimpleTestCase):
def render_template(self, tpl, context=None):
if context is None:
context = {}
return (
Template("{% load admin_interface_tags %}" + tpl)
.render(Context(context))
.strip()
)

def test_returns_existing_variable(self):
out = self.render_template(
'{% admin_interface_resolve_variable "myvar" as res %}{{ res }}',
{"myvar": "hello"},
)
self.assertEqual(out, "hello")

def test_returns_default_when_missing(self):
out = self.render_template(
'{% admin_interface_resolve_variable "missingvar" as res %}{{ res }}'
)
self.assertEqual(out, "")

def test_returns_custom_default(self):
out = self.render_template(
'{% admin_interface_resolve_variable "missingvar" "def" as res %}{{ res }}'
)
self.assertEqual(out, "def")

def test_dotted_variable_existing(self):
context = {"user": {"name": "alice"}}
out = self.render_template(
'{% admin_interface_resolve_variable "user.name" as res %}{{ res }}',
context,
)
self.assertEqual(out, "alice")

def test_dotted_variable_missing_middle_key(self):
context = {"user": {}}
out = self.render_template(
'{% admin_interface_resolve_variable "user.name" "def" as res %}{{ res }}',
context,
)
self.assertEqual(out, "def")

def test_dotted_variable_missing_top_key(self):
out = self.render_template(
'{% admin_interface_resolve_variable "user.name" "guest" as res %}{{ res }}'
)
self.assertEqual(out, "guest")

def test_dotted_variable_with_object_attribute(self):
class User:
def __init__(self):
self.name = "bob"

context = {"user": User()}
out = self.render_template(
'{% admin_interface_resolve_variable "user.name" as res %}{{ res }}',
context,
)
self.assertEqual(out, "bob")

def test_dotted_variable_partial_attribute_missing(self):
class User:
pass

context = {"user": User()}
out = self.render_template(
'{% admin_interface_resolve_variable "user.name" as res %}{{ res }}',
context,
)
self.assertEqual(out, "")

def test_non_dict_non_object_root(self):
context = {"user": "notadict"}
out = self.render_template(
'{% admin_interface_resolve_variable "user.name" as res %}{{ res }}',
context,
)
self.assertEqual(out, "")

def test_dotted_variable_object_with_nested_dict(self):
class User:
def __init__(self):
self.info = {"name": "bob"}

context = {"user": User()}
out = self.render_template(
'{% admin_interface_resolve_variable "user.info.name" as res %}{{ res }}',
context,
)
self.assertEqual(out, "bob")