Skip to content

Conversation

hazra1991
Copy link


name: Pull request
about: Submit a pull request for this project
assignees: fabiocaccamo


Describe your changes
This PR adds a resolve_variable template tag to safely handle missing context variables in base_site.html, preventing VariableDoesNotExist log noise.

  • resolve_variable returns an empty string or a custom default instead of raising exceptions.
  • Updated base_site.html to use it for adminform and inline_admin_formsets.
  • Added tests to ensure correct behavior with existing, missing, and default values.

Missing context variables are harmless, but with template logging they generate VariableDoesNotExist logs, creating noise. resolve_variable can prevent this when used as a guard.

Alternative :-
In our production, we suppressed via Django’s LOGGING config in settings.py:

LOGGING = {
    "loggers": {
        "django.template": {
            "handlers": ["null"],
            "level": "WARNING",
            "propagate": False
        }
    }
}

Related issue
#446

Checklist before requesting a review

  • I have performed a self-review of my code.
  • I have added tests for the proposed changes.
  • I have run the tests and there are not errors.

Copy link

codecov bot commented Sep 4, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.46%. Comparing base (a9fcae6) to head (9c1cd7b).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #453      +/-   ##
==========================================
+ Coverage   97.39%   97.46%   +0.07%     
==========================================
  Files          38       38              
  Lines         422      434      +12     
==========================================
+ Hits          411      423      +12     
  Misses         11       11              
Flag Coverage Δ
unittests 97.46% <100.00%> (+0.07%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fabiocaccamo fabiocaccamo requested a review from Copilot September 4, 2025 20:23
Copilot

This comment was marked as resolved.

@fabiocaccamo fabiocaccamo self-requested a review September 4, 2025 20:27
@fabiocaccamo fabiocaccamo moved this to In Progress in Open Source Sep 4, 2025
Repository owner deleted a comment from Copilot AI Sep 4, 2025
@@ -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=""):
Copy link
Owner

Choose a reason for hiding this comment

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

Could you please rename the template tag to admin_interface_resolve_variable?

Copy link
Author

Choose a reason for hiding this comment

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

Done
renamed the template tag to admin_interface_resolve_variable as suggested.

@@ -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)
Copy link
Owner

Choose a reason for hiding this comment

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

This works only for top-level context keys. It doesn't resolve dotted variables like user.username, the correct approach is:

try:
    return template.Variable(var_name).resolve(context)
except VariableDoesNotExist:
    return default

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 suggestion!

I initially considered using template.Variable(var_name).resolve(context) directly, but the issue is that when a variable (or part of a dotted lookup like var.dottedvar) is missing, Variable.resolve() raises VariableDoesNotExist, which gets logged by Django and pollutes the logs. which brings us to the same issue that we are trying to prevent .

To avoid this, I’ve added a lightweight pre-check that verifies variable (normal and dotted) exists in the context.

If it does, I then delegate to Variable.resolve() so Django still handles callables, translations, etc. If not, I return the default immediately.

@fabiocaccamo fabiocaccamo added enhancement New feature or request bug Something isn't working and removed enhancement New feature or request labels Sep 4, 2025
@hazra1991
Copy link
Author

Based on the above comments Kindly let me know If anything needed to be done or changed or explained.

New test scenarios are added based on the review and changes

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 !

Comment on lines +193 to +203
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
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.

@hazra1991
Copy link
Author

Kindly let me know you thoughts on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

2 participants