Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 7 additions & 8 deletions src/core/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class Meta:
"status": forms.Select(attrs={"class": "select validator w-full"}),
"pub_year": forms.NumberInput(attrs={"class": "input validator w-full", "placeholder": _("YYYY")}),
"score": StarRatingWidget(attrs={"class": "validator"}),
"review": MDEWidget(),
"review": MDEWidget(options={"nativeSpellcheck": True, "inputStyle": "contenteditable"}),
"review_date": forms.TextInput(
attrs={
"class": "input validator w-full",
Expand All @@ -70,16 +70,15 @@ def __init__(self, *args, **kwargs):
"""
Initialize the form and add HTMX attributes for dynamic field validation.

Configures all form fields (except cover and contributors) with HTMX
attributes to enable real-time validation on user input.
Enables real-time HTMX validation only on free-text fields where user
input can actually be invalid: title (required), external_uri (URL
format), pub_year (min/max range), and review_date (date format).
"""
super().__init__(*args, **kwargs)
# Add HTMX attributes for dynamic validation
validation_url = reverse("media_validate_field")
for field_name, field in self.fields.items():
# Do not add dynamic validation on file or M2M fields (cover, contributors, tags)
if field_name in ["cover", "contributors", "tags"]:
continue
validated_fields = ["title", "external_uri", "pub_year", "review_date"]
for field_name in validated_fields:
field = self.fields[field_name]
field.widget.attrs.update(
{
"hx-post": validation_url,
Expand Down
17 changes: 7 additions & 10 deletions src/tests/core/test_htmx_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,11 @@ def test_htmx_validate_field_valid(logged_in_client):


@pytest.mark.django_db
def test_htmx_validate_field_no_validation_for_cover_contributors(logged_in_client):
def test_htmx_validate_field_blank_allowed_fields(logged_in_client):
url = reverse("media_validate_field")
# cover and contributors should not trigger validation markup
data = {"field_name": "cover"}
response = logged_in_client.post(url, data, HTTP_HX_REQUEST="true")
assert response.status_code == 200
assert response.content.decode().strip() == ""
data = {"field_name": "contributors"}
response = logged_in_client.post(url, data, HTTP_HX_REQUEST="true")
assert response.status_code == 200
assert response.content.decode().strip() == ""
# Fields with blank=True return no error when submitted empty
for field_name in ["cover", "contributors", "review", "score"]:
data = {"field_name": field_name}
response = logged_in_client.post(url, data, HTTP_HX_REQUEST="true")
assert response.status_code == 200
assert response.content.decode().strip() == "", f"Expected no error for {field_name}"
11 changes: 6 additions & 5 deletions src/tests/core/test_mediaform_htmx.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@
@pytest.mark.django_db
def test_mediaform_htmx_attrs():
form = MediaForm()
validated_fields = {"title", "external_uri", "pub_year", "review_date"}
for field_name, field in form.fields.items():
widget = field.widget
if field_name in ["cover", "contributors", "tags"]:
# These fields should not have HTMX attributes
for attr in ["hx-post", "hx-trigger", "hx-target", "hx-include", "hx-vals"]:
assert attr not in widget.attrs
else:
if field_name in validated_fields:
assert widget.attrs.get("hx-post"), f"hx-post missing on {field_name}"
assert widget.attrs.get("hx-trigger"), f"hx-trigger missing on {field_name}"
assert widget.attrs.get("hx-target"), f"hx-target missing on {field_name}"
assert widget.attrs.get("hx-include"), f"hx-include missing on {field_name}"
assert widget.attrs.get("hx-vals"), f"hx-vals missing on {field_name}"
else:
# All other fields should not have HTMX validation attributes
for attr in ["hx-post", "hx-trigger", "hx-target", "hx-include", "hx-vals"]:
assert attr not in widget.attrs, f"{attr} should not be on {field_name}"
Loading
Loading