Edit Form from a Database Row¶
Show a saved record in a form, let the user change it, and save it back.
models.py
from datetime import date
from enum import Enum
from pydantic_schemaforms import Field, FormModel
class Status(str, Enum):
draft = 'draft'
active = 'active'
archived = 'archived'
class ProductForm(FormModel):
name: str = Field(title='Name', min_length=2)
price: float = Field(title='Price', ge=0)
status: Status = Field(Status.draft, title='Status')
launch_date: date | None = Field(None, title='Launch date')
featured: bool = Field(False, title='Featured on the home page')
PRODUCTS = { # stands in for your database
1: {
'id': 1,
'name': 'Desk lamp',
'price': 39.5,
'status': Status.active,
'launch_date': date(2026, 3, 1),
'featured': True,
'created_by': 'ada',
},
}
main.py
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from models import PRODUCTS, ProductForm
from pydantic_schemaforms import render_form_html_async
app = FastAPI()
ASSETS = {'include_framework_assets': True, 'asset_mode': 'cdn'}
def load_product(product_id: int) -> dict:
if product_id not in PRODUCTS:
raise HTTPException(status_code=404)
return PRODUCTS[product_id]
@app.get('/products/{product_id}/edit', response_class=HTMLResponse)
async def edit_product(product_id: int):
row = load_product(product_id)
return await render_form_html_async(
ProductForm,
form_data={name: row[name] for name in ProductForm.model_fields},
submit_url=f'/products/{product_id}/edit',
**ASSETS,
)
@app.post('/products/{product_id}/edit', response_class=HTMLResponse)
async def save_product(product_id: int, request: Request):
row = load_product(product_id)
result = ProductForm.validate(
await request.form(), submit_url=f'/products/{product_id}/edit', **ASSETS
)
if not result.is_valid:
return await result.render_with_errors_async()
row.update(result.data)
return RedirectResponse(f'/products/{product_id}/edit', status_code=303)
main.py
from flask import Flask, abort, redirect, request
from models import PRODUCTS, ProductForm
from pydantic_schemaforms import render_form_html
app = Flask(__name__)
ASSETS = {'include_framework_assets': True, 'asset_mode': 'cdn'}
def load_product(product_id: int) -> dict:
if product_id not in PRODUCTS:
abort(404)
return PRODUCTS[product_id]
@app.get('/products/<int:product_id>/edit')
def edit_product(product_id: int):
row = load_product(product_id)
return render_form_html(
ProductForm,
form_data={name: row[name] for name in ProductForm.model_fields},
submit_url=f'/products/{product_id}/edit',
**ASSETS,
)
@app.post('/products/<int:product_id>/edit')
def save_product(product_id: int):
row = load_product(product_id)
result = ProductForm.validate(request.form, submit_url=f'/products/{product_id}/edit', **ASSETS)
if not result.is_valid:
return result.render_with_errors()
row.update(result.data)
return redirect(f'/products/{product_id}/edit', code=303)
How it works¶
form_data=fills the form. Pass the row's values as they are: dates, Enum members and numbers are shown correctly, so there's nothing to convert.- Only the form's fields are passed. The row also has
idandcreated_by, which the user must not edit. They are left out ofform_data, and since they aren't fields ofProductForm, a request that sends them anyway can't change them:validate()ignores unknown keys. result.datahas the same keys as the model, typed (Status.archived,date(2026, 3, 1),Nonefor a cleared date), ready to write back.
If your rows are ORM objects or Pydantic models, build the dictionary the same
way: {name: getattr(row, name) for name in ProductForm.model_fields}.