diff --git a/src/unfold_fobi/api/views.py b/src/unfold_fobi/api/views.py index c0b84d3..6d202c1 100644 --- a/src/unfold_fobi/api/views.py +++ b/src/unfold_fobi/api/views.py @@ -1,5 +1,6 @@ """DRF API views for unfold_fobi.""" +import datetime from django.apps import apps as django_apps from django.core.exceptions import ObjectDoesNotExist from django.forms.fields import EmailField @@ -13,6 +14,17 @@ from rest_framework.response import Response +def _serialize_initial(value): + """Return a JSON-friendly initial value for API responses.""" + if callable(value): + value = value() + if value is None: + return None + if isinstance(value, (datetime.date, datetime.datetime)): + return value.isoformat() + return value + + def normalize_field_choices(field, field_name): try: optgroups = field.widget.optgroups(field_name, field.initial) @@ -159,6 +171,7 @@ def get_form_fields(request, slug): } for field_name, field in fields.items(): + widget_attrs = getattr(getattr(field, "widget", None), "attrs", {}) field_info = { "name": field_name, "type": field.__class__.__name__, @@ -168,6 +181,14 @@ def get_form_fields(request, slug): "help_text": getattr(field, "help_text", ""), } + placeholder = widget_attrs.get("placeholder") + if placeholder not in (None, ""): + field_info["placeholder"] = placeholder + + initial = _serialize_initial(getattr(field, "initial", None)) + if initial is not None: + field_info["initial"] = initial + if hasattr(field, "choices") and field.choices: field_info["choices"] = normalize_field_choices(field, field_name) diff --git a/tests/api/test_form_fields.py b/tests/api/test_form_fields.py index b961f96..5200876 100644 --- a/tests/api/test_form_fields.py +++ b/tests/api/test_form_fields.py @@ -117,6 +117,49 @@ def test_select_field_has_choices(self, admin_client, multi_field_form): values = [c["value"] for c in f["choices"]] assert "r" in values + @pytest.fixture() + def metadata_form(self, db, admin_user): + entry = FormEntry.objects.create( + user=admin_user, + name="Metadata Form", + slug="metadata-form", + is_public=True, + ) + FormElementEntry.objects.create( + form_entry=entry, + plugin_uid="text", + plugin_data=json.dumps( + { + "label": "Full Name", + "name": "full_name", + "required": True, + "placeholder": "Enter your name", + } + ), + position=1, + ) + FormElementEntry.objects.create( + form_entry=entry, + plugin_uid="date", + plugin_data=json.dumps( + { + "label": "Initial Date", + "name": "initial", + "required": False, + "initial": "2026-04-16", + } + ), + position=2, + ) + FormHandlerEntry.objects.get_or_create(form_entry=entry, plugin_uid="db_store") + return entry + + def test_includes_placeholder_and_initial(self, admin_client, metadata_form): + fields = _get_fields(admin_client, metadata_form.slug) + + assert fields["full_name"]["placeholder"] == "Enter your name" + assert fields["initial"]["initial"] == "2026-04-16" + class TestFormFieldsWidgetDisambiguation: """Widget key is correct for every registered fobi field plugin."""