-
-
Notifications
You must be signed in to change notification settings - Fork 142
Description
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 liketrue
,false
, or function names.
- Quoted strings (
- 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.