Skip to content

FormBuilder.from_pydantic (Beta)

FormBuilder.from_pydantic() generates a Bootstrap-styled form from a Pydantic model.

Pydantic v2 required

FormBuilder.from_pydantic() reads model_fields, which is the Pydantic v2 model API. Pydantic v1 models are not supported by this helper.

Naming update in v0.6.1

Starting in Faststrap v0.6.1, the preferred import is FormBuilder to avoid confusion with FastHTML's native Form element.

  • v0.6.1+: from faststrap import FormBuilder
  • v0.6.0 and earlier: from faststrap import Form
  • Form remains available as a compatibility alias, but new code should prefer FormBuilder.

Import

from faststrap import FormBuilder

Basic Usage

from pydantic import BaseModel, EmailStr

class Signup(BaseModel):
    email: EmailStr
    age: int
    marketing_opt_in: bool = False

form = FormBuilder.from_pydantic(Signup, action="/signup")

Supported Field Mapping (MVP)

  • str -> text input
  • EmailStr -> email input
  • int -> number input
  • float -> number input (step="any")
  • bool -> checkbox
  • Literal[...] -> select
  • Enum -> select

Options

Parameter Type Default Description
model_class type[Any] required Pydantic BaseModel class.
action str \| None None Form submit URL.
method str "post" Form method.
include list[str] \| None None Include only selected fields.
exclude list[str] \| None None Exclude selected fields.
submit_label str "Submit" Submit button text.
submit_variant str "primary" Bootstrap button variant.
form_cls str "" Extra form classes.
button_cls str "" Extra submit button classes.
**kwargs Any Extra form attributes, including HTMX attributes.

HTMX Submit Example

FormBuilder.from_pydantic(
    Signup,
    hx_post="/signup",
    hx_target="#signup-result",
    hx_swap="outerHTML",
    submit_label="Create account",
)

Validation Flow

Use FormErrorSummary, FormGroupFromErrors, LiveValidationField, and ValidationMessage when you need server-side error rendering or live field validation. FormBuilder.from_pydantic() focuses on generating the initial Bootstrap-styled form.

Backward Compatibility

If you are maintaining a project pinned below v0.6.1, this older import still works:

from faststrap import Form

form = Form.from_pydantic(Signup, action="/signup")

API Reference

faststrap.components.forms.form.FormBuilder

Form builder helpers.

Source code in src/faststrap/components/forms/form.py
@beta
class FormBuilder:
    """Form builder helpers."""

    @staticmethod
    def from_pydantic(
        model_class: type[Any],
        *,
        action: str | None = None,
        method: str = "post",
        include: list[str] | None = None,
        exclude: list[str] | None = None,
        submit_label: str = "Submit",
        submit_variant: str = "primary",
        form_cls: str = "",
        button_cls: str = "",
        **kwargs: Any,
    ) -> FTForm:
        """Generate a Bootstrap-styled form from a Pydantic model class."""
        try:
            pydantic_module = importlib.import_module("pydantic")
        except ImportError as exc:
            msg = (
                "FormBuilder.from_pydantic() requires pydantic. "
                "Install with `pip install pydantic`."
            )
            raise ImportError(msg) from exc

        base_model = pydantic_module.BaseModel
        if not isinstance(model_class, type) or not issubclass(model_class, base_model):
            msg = "FormBuilder.from_pydantic() expects a Pydantic BaseModel class."
            raise TypeError(msg)

        model_fields = getattr(model_class, "model_fields", None)
        if not isinstance(model_fields, dict):
            msg = "Unsupported Pydantic model format."
            raise TypeError(msg)

        include_set = set(include or model_fields.keys())
        exclude_set = set(exclude or [])

        fields: list[Any] = []
        for name, field_info in model_fields.items():
            if name not in include_set or name in exclude_set:
                continue

            input_element, required = _build_field_input(name, field_info)
            label = getattr(field_info, "title", None) or _pretty_label(name)
            description = getattr(field_info, "description", None)
            fields.append(
                FormGroup(
                    input_element,
                    label=label,
                    help_text=description,
                    required=required,
                )
            )

        fields.append(
            FTButton(
                submit_label,
                type="submit",
                cls=f"btn btn-{submit_variant} {button_cls}".strip(),
            )
        )

        form_attrs: dict[str, Any] = {
            "method": method,
            "cls": f"faststrap-generated-form {form_cls}".strip(),
        }
        if action:
            form_attrs["action"] = action
        form_attrs.update(kwargs)
        form_attrs = convert_attrs(form_attrs)

        return FTForm(*fields, **form_attrs)
from_pydantic(model_class, *, action=None, method='post', include=None, exclude=None, submit_label='Submit', submit_variant='primary', form_cls='', button_cls='', **kwargs) staticmethod

Generate a Bootstrap-styled form from a Pydantic model class.

Source code in src/faststrap/components/forms/form.py
@staticmethod
def from_pydantic(
    model_class: type[Any],
    *,
    action: str | None = None,
    method: str = "post",
    include: list[str] | None = None,
    exclude: list[str] | None = None,
    submit_label: str = "Submit",
    submit_variant: str = "primary",
    form_cls: str = "",
    button_cls: str = "",
    **kwargs: Any,
) -> FTForm:
    """Generate a Bootstrap-styled form from a Pydantic model class."""
    try:
        pydantic_module = importlib.import_module("pydantic")
    except ImportError as exc:
        msg = (
            "FormBuilder.from_pydantic() requires pydantic. "
            "Install with `pip install pydantic`."
        )
        raise ImportError(msg) from exc

    base_model = pydantic_module.BaseModel
    if not isinstance(model_class, type) or not issubclass(model_class, base_model):
        msg = "FormBuilder.from_pydantic() expects a Pydantic BaseModel class."
        raise TypeError(msg)

    model_fields = getattr(model_class, "model_fields", None)
    if not isinstance(model_fields, dict):
        msg = "Unsupported Pydantic model format."
        raise TypeError(msg)

    include_set = set(include or model_fields.keys())
    exclude_set = set(exclude or [])

    fields: list[Any] = []
    for name, field_info in model_fields.items():
        if name not in include_set or name in exclude_set:
            continue

        input_element, required = _build_field_input(name, field_info)
        label = getattr(field_info, "title", None) or _pretty_label(name)
        description = getattr(field_info, "description", None)
        fields.append(
            FormGroup(
                input_element,
                label=label,
                help_text=description,
                required=required,
            )
        )

    fields.append(
        FTButton(
            submit_label,
            type="submit",
            cls=f"btn btn-{submit_variant} {button_cls}".strip(),
        )
    )

    form_attrs: dict[str, Any] = {
        "method": method,
        "cls": f"faststrap-generated-form {form_cls}".strip(),
    }
    if action:
        form_attrs["action"] = action
    form_attrs.update(kwargs)
    form_attrs = convert_attrs(form_attrs)

    return FTForm(*fields, **form_attrs)

faststrap.components.forms.form.Form

Compatibility wrapper for FastHTML's native Form element.

Form(...) delegates to FastHTML's native Form so wildcard imports do not accidentally break ordinary form markup. Form.from_pydantic() is retained for backward compatibility with Faststrap v0.6.0 and earlier. New code should prefer FormBuilder.from_pydantic() for generated forms.

Source code in src/faststrap/components/forms/form.py
@beta
class Form:
    """Compatibility wrapper for FastHTML's native `Form` element.

    `Form(...)` delegates to FastHTML's native `Form` so wildcard imports do
    not accidentally break ordinary form markup. `Form.from_pydantic()` is
    retained for backward compatibility with Faststrap v0.6.0 and earlier.
    New code should prefer `FormBuilder.from_pydantic()` for generated forms.
    """

    def __new__(cls, *children: Any, **kwargs: Any) -> FTForm:
        return FTForm(*children, **kwargs)

    @staticmethod
    def from_pydantic(
        model_class: type[Any],
        *,
        action: str | None = None,
        method: str = "post",
        include: list[str] | None = None,
        exclude: list[str] | None = None,
        submit_label: str = "Submit",
        submit_variant: str = "primary",
        form_cls: str = "",
        button_cls: str = "",
        **kwargs: Any,
    ) -> FTForm:
        warnings.warn(
            "Form.from_pydantic() is deprecated as of Faststrap v0.6.1; "
            "use FormBuilder.from_pydantic() instead. "
            "The Form alias remains supported for backward compatibility.",
            DeprecationWarning,
            stacklevel=2,
        )
        return FormBuilder.from_pydantic(
            model_class,
            action=action,
            method=method,
            include=include,
            exclude=exclude,
            submit_label=submit_label,
            submit_variant=submit_variant,
            form_cls=form_cls,
            button_cls=button_cls,
            **kwargs,
        )