Description
The render_field template tag in django-widget-tweaks fails to parse complex attribute values, such as Alpine.js ::class bindings (e.g., ::class="{'is-invalid': formFields.username.errors.length}"). This results in a TemplateSyntaxError with the message Could not parse the remainder: '"{'' from '"{''. The issue arises because the ATTRIBUTE_RE regex in widget_tweaks/templatetags/widget_tweaks.py stops parsing at the first quote it encounters, causing the regex match to be incomplete.
This issue affects users integrating Django with frontend frameworks like Alpine.js, where such bindings are common. Without quotes around the class key (e.g., ::class="{is-invalid: ...}"), Django parses the template correctly, but Alpine.js fails because it expects a string key, leading to a runtime error.
Steps to Reproduce
- Create a Django template with the following code:
{% load widget_tweaks %}
{% render_field form.username ::class="{'is-invalid': isInvalid}" %}
- Render the template with Django 5.2.1 (or similar).
- Observe the error:
TemplateSyntaxError at /path/
Could not parse the remainder: '"{'' from '"{''`
Expected Behavior
The render_field tag should correctly parse complex attribute values, including JavaScript object literals with nested quotes, and render the attribute as-is for frontend frameworks like Alpine.js to process.
Actual Behavior
The ATTRIBUTE_RE regex ([@\w:_\.-]+\+?=['"]?[^"']*['"]?) stops parsing at the first quote in the value, causing a TemplateSyntaxError for expressions like {'is-invalid': ...}.
Proposed Solution
Modify the ATTRIBUTE_RE regex in widget_tweaks/templatetags/widget_tweaks.py to handle quoted strings, curly-brace-enclosed expressions, and unquoted values more robustly. Here's a proposed regex:
import re
ATTRIBUTE_RE = re.compile(
r"""
(?P<attr>
[@\w:_\.-]+
)
(?P<sign>
\+?=
)
(?P<value>
(?:
['"][^'"]*['"] # Match quoted strings
|
\{[^}]*\} # Match content within curly braces
|
[^'"\s=]+ # Match unquoted content
)*
)
""",
re.VERBOSE | re.UNICODE,
)
Explanation of Proposed Changes
- The
value group now supports:
- Quoted strings (
['"][^'"]*['"]): Matches single- or double-quoted strings.
- Curly-brace content (
\{[^}]*\}): Matches JavaScript object literals or similar expressions.
- Unquoted content (
[^'"\s=]+): Matches simple values like true, false, or function names.
- The
(...)* allows multiple such patterns to be combined, supporting complex expressions.
- This change maintains backward compatibility with existing attribute-value pairs (e.g.,
class="form-control") while enabling support for Alpine.js bindings.
Environment
- Django Version: 5.2.1
- Python Version: 3.12.3
- django-widget-tweaks Version: 1.5.1
- Frontend Framework: Alpine.js
Additional Notes
- The proposed regex assumes balanced curly braces. Edge cases with deeply nested or malformed expressions may require further refinement.
Would the maintainers be open to a pull request implementing this change? I can provide a PR with the updated regex and tests if desired.
Description
The
render_fieldtemplate tag indjango-widget-tweaksfails to parse complex attribute values, such as Alpine.js::classbindings (e.g.,::class="{'is-invalid': formFields.username.errors.length}"). This results in aTemplateSyntaxErrorwith the messageCould not parse the remainder: '"{'' from '"{''. The issue arises because theATTRIBUTE_REregex inwidget_tweaks/templatetags/widget_tweaks.pystops parsing at the first quote it encounters, causing the regex match to be incomplete.This issue affects users integrating Django with frontend frameworks like Alpine.js, where such bindings are common. Without quotes around the class key (e.g.,
::class="{is-invalid: ...}"), Django parses the template correctly, but Alpine.js fails because it expects a string key, leading to a runtime error.Steps to Reproduce
{% load widget_tweaks %} {% render_field form.username ::class="{'is-invalid': isInvalid}" %}Expected Behavior
The
render_fieldtag should correctly parse complex attribute values, including JavaScript object literals with nested quotes, and render the attribute as-is for frontend frameworks like Alpine.js to process.Actual Behavior
The
ATTRIBUTE_REregex ([@\w:_\.-]+\+?=['"]?[^"']*['"]?) stops parsing at the first quote in the value, causing aTemplateSyntaxErrorfor expressions like{'is-invalid': ...}.Proposed Solution
Modify the
ATTRIBUTE_REregex inwidget_tweaks/templatetags/widget_tweaks.pyto handle quoted strings, curly-brace-enclosed expressions, and unquoted values more robustly. Here's a proposed regex:Explanation of Proposed Changes
valuegroup now supports:['"][^'"]*['"]): Matches single- or double-quoted strings.\{[^}]*\}): Matches JavaScript object literals or similar expressions.[^'"\s=]+): Matches simple values liketrue,false, or function names.(...)*allows multiple such patterns to be combined, supporting complex expressions.class="form-control") while enabling support for Alpine.js bindings.Environment
Additional Notes
Would the maintainers be open to a pull request implementing this change? I can provide a PR with the updated regex and tests if desired.