From f8e451afe34be0045c6cc454a55bc349378c9560 Mon Sep 17 00:00:00 2001 From: "ahazra.ext" Date: Thu, 4 Sep 2025 14:34:53 +0000 Subject: [PATCH 1/4] Add resolve_variable simple_tag for missing template vars --- admin_interface/templates/admin/base_site.html | 3 +++ admin_interface/templatetags/admin_interface_tags.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/admin_interface/templates/admin/base_site.html b/admin_interface/templates/admin/base_site.html index 4b4ad14f..fe5afea6 100644 --- a/admin_interface/templates/admin/base_site.html +++ b/admin_interface/templates/admin/base_site.html @@ -146,6 +146,9 @@ {% if theme.list_filter_highlight %} list-filter-highlight {% endif %} {% if theme.list_filter_sticky %} list-filter-sticky {% endif %} +{% resolve_variable "adminform" as adminform %} +{% 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 %} diff --git a/admin_interface/templatetags/admin_interface_tags.py b/admin_interface/templatetags/admin_interface_tags.py index a675dff1..613051ca 100644 --- a/admin_interface/templatetags/admin_interface_tags.py +++ b/admin_interface/templatetags/admin_interface_tags.py @@ -188,6 +188,11 @@ def admin_interface_use_changeform_tabs(adminform, inline_forms): return has_tabs +@register.simple_tag(takes_context=True) +def resolve_variable(context, var_name, default=""): + return context.get(var_name, default) + + @register.filter def admin_interface_slugify(name): return slugify(str(name or "")) From d5901ea8b09fdbf8d0d660d6b3dd43a33a71c879 Mon Sep 17 00:00:00 2001 From: "ahazra.ext" Date: Thu, 4 Sep 2025 15:04:19 +0000 Subject: [PATCH 2/4] Add test for resolve_variable --- tests/test_resolve_variable.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tests/test_resolve_variable.py diff --git a/tests/test_resolve_variable.py b/tests/test_resolve_variable.py new file mode 100644 index 00000000..f6538f0d --- /dev/null +++ b/tests/test_resolve_variable.py @@ -0,0 +1,31 @@ +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( + '{% resolve_variable "myvar" as result %}{{ result }}', {"myvar": "hello"} + ) + self.assertEqual(out, "hello") + + def test_returns_default_when_missing(self): + out = self.render_template( + '{% resolve_variable "missingvar" as result %}{{ result }}' + ) + self.assertEqual(out, "") + + def test_returns_custom_default(self): + out = self.render_template( + '{% resolve_variable "missingvar" "fallback" as result %}{{ result }}' + ) + self.assertEqual(out, "fallback") From bd9167dbd0bb767dbf108c32846ac6cd19fa4bfd Mon Sep 17 00:00:00 2001 From: "ahazra.ext" Date: Fri, 5 Sep 2025 16:46:53 +0000 Subject: [PATCH 3/4] refactor: rename resolve_variable and improve resolution logic --- .../templates/admin/base_site.html | 4 +- .../templatetags/admin_interface_tags.py | 16 +++- tests/test_resolve_variable.py | 74 ++++++++++++++++++- 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/admin_interface/templates/admin/base_site.html b/admin_interface/templates/admin/base_site.html index fe5afea6..dff51a83 100644 --- a/admin_interface/templates/admin/base_site.html +++ b/admin_interface/templates/admin/base_site.html @@ -146,8 +146,8 @@ {% if theme.list_filter_highlight %} list-filter-highlight {% endif %} {% if theme.list_filter_sticky %} list-filter-sticky {% endif %} -{% resolve_variable "adminform" as adminform %} -{% resolve_variable "inline_admin_formsets" as inline_admin_formsets %} +{% 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 %} diff --git a/admin_interface/templatetags/admin_interface_tags.py b/admin_interface/templatetags/admin_interface_tags.py index 613051ca..0fe9df2d 100644 --- a/admin_interface/templatetags/admin_interface_tags.py +++ b/admin_interface/templatetags/admin_interface_tags.py @@ -189,8 +189,20 @@ def admin_interface_use_changeform_tabs(adminform, inline_forms): @register.simple_tag(takes_context=True) -def resolve_variable(context, var_name, default=""): - return context.get(var_name, default) +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 + + return template.Variable(var_name).resolve(context) @register.filter diff --git a/tests/test_resolve_variable.py b/tests/test_resolve_variable.py index f6538f0d..b2106cd5 100644 --- a/tests/test_resolve_variable.py +++ b/tests/test_resolve_variable.py @@ -14,18 +14,84 @@ def render_template(self, tpl, context=None): def test_returns_existing_variable(self): out = self.render_template( - '{% resolve_variable "myvar" as result %}{{ result }}', {"myvar": "hello"} + '{% 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( - '{% resolve_variable "missingvar" as result %}{{ result }}' + '{% admin_interface_resolve_variable "missingvar" as result %}{{ result }}' ) self.assertEqual(out, "") def test_returns_custom_default(self): out = self.render_template( - '{% resolve_variable "missingvar" "fallback" as result %}{{ result }}' + '{% admin_interface_resolve_variable "missingvar" "def" as res %}{{ res }}' ) - self.assertEqual(out, "fallback") + 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") From 9c1cd7b1e89460e08242137d47712cbcca96879d Mon Sep 17 00:00:00 2001 From: "ahazra.ext" Date: Sun, 7 Sep 2025 04:15:27 +0000 Subject: [PATCH 4/4] refactor: variable name --- tests/test_resolve_variable.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_resolve_variable.py b/tests/test_resolve_variable.py index b2106cd5..15e49413 100644 --- a/tests/test_resolve_variable.py +++ b/tests/test_resolve_variable.py @@ -14,14 +14,14 @@ def render_template(self, tpl, context=None): def test_returns_existing_variable(self): out = self.render_template( - '{% admin_interface_resolve_variable "myvar" as result %}{{ result }}', + '{% 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 result %}{{ result }}' + '{% admin_interface_resolve_variable "missingvar" as res %}{{ res }}' ) self.assertEqual(out, "") @@ -34,7 +34,7 @@ def test_returns_custom_default(self): def test_dotted_variable_existing(self): context = {"user": {"name": "alice"}} out = self.render_template( - '{% admin_interface_resolve_variable "user.name" as result %}{{ result }}', + '{% admin_interface_resolve_variable "user.name" as res %}{{ res }}', context, ) self.assertEqual(out, "alice") @@ -60,7 +60,7 @@ def __init__(self): context = {"user": User()} out = self.render_template( - '{% admin_interface_resolve_variable "user.name" as result %}{{ result }}', + '{% admin_interface_resolve_variable "user.name" as res %}{{ res }}', context, ) self.assertEqual(out, "bob")