Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
Copy link
Owner

Choose a reason for hiding this comment

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

Just a small thing: please use always the same variable name for coherence. I see sometimes you use res, sometimes result. Better to choose one (result) and use it everywhere to keep the code more clear.

Copy link
Author

Choose a reason for hiding this comment

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

Acknowledged, and thank you for pointing that out !

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 result %}{{ result }}',
{"myvar": "hello"},
)
self.assertEqual(out, "hello")

def test_returns_default_when_missing(self):
out = self.render_template(
'{% admin_interface_resolve_variable "missingvar" as result %}{{ result }}'
)
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 result %}{{ result }}',
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 result %}{{ result }}',
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")