2.5 KiB
Profile Form Validation Design
Problem
The self-service (/manage/profile) and admin (/admin/users/{id}/profile) profile
forms have minimal validation: email only checks for @, phone number has no format
validation at all. Validation logic is duplicated inline in both route handlers.
Decision
Use Pydantic types for field validation via a shared ProfileUpdate model.
- Email:
pydantic.EmailStr(usesemail-validatorunder the hood) - Phone:
pydantic_extra_types.phone_numbers.PhoneNumberValidatorwithnumber_format="E164"— accepts various input formats, normalizes to strict E.164 (+46701234567). Backed by Google'slibphonenumber. - Picture URL: custom Pydantic validator (http/https scheme, has netloc)
- Field lengths:
Field(max_length=...)on each field
New dependency
pydantic-extra-types[phonenumbers] — brings in the phonenumbers package.
Shared validation module
src/porchlight/validation.py:
from typing import Annotated
from pydantic import BaseModel, EmailStr, Field
from pydantic_extra_types.phone_numbers import PhoneNumberValidator
E164Phone = Annotated[
str, PhoneNumberValidator(number_format="E164")
]
class ProfileUpdate(BaseModel):
given_name: str = Field(default="", max_length=255)
family_name: str = Field(default="", max_length=255)
preferred_username: str = Field(default="", max_length=255)
email: EmailStr | None = None
phone_number: E164Phone | None = None
picture: str = Field(default="", max_length=2048) # + URL validator
locale: str = Field(default="", max_length=20)
Route handlers instantiate ProfileUpdate from form data, catch ValidationError,
and return the first user-friendly error as the existing HTMLResponse with
role="alert".
Route changes
Both manage/routes.py and admin/routes.py profile POST handlers will:
- Parse form data through
ProfileUpdate - Use validated/normalized values from the model
- Replace all inline validation with a single try/except
Template changes
Add pattern and title attributes on the phone input for browser-native hints
about E.164 format.
Validation: server-side only
No client-side JS validation. The HTML type="email" and type="tel" attributes
already provide basic browser hints. Server-side validation via htmx response
handles all error display.
Tests
- Unit tests for
ProfileUpdate(valid/invalid emails, phones, URLs, lengths) - Integration tests for manage profile POST (happy path + validation errors)
- Existing admin tests may be tightened