Reforms
Documentation: https://reforms.boardpack.org
Source Code: https://github.com/boardpack/reforms
Reforms is a fresh pydantic-based forms validation and rendering library for Python 3.6+.
The key features are:
- Familiar: Expanded Pydantic retaining all data validation and model creation capabilities.
- Easy: Designed to be easy to use and learn. Less time reading docs.
- Theming: Supported the usage of existing template pack and the creation of your own.
Requirements¶
Python 3.6+
Reforms has the next hard dependencies:
Installation¶
$ pip install git+http://github.com/boardpack/reforms
---> 100%
Example¶
In the next example, we will use FastAPI
as a web framework. So you need to install fastapi
and uvicorn
first. Also, you
need to install python-multipart
library to turn on forms support in FastAPI.
$ pip install fastapi uvicorn python-multipart
---> 100%
Create it¶
- Create a file
models.py
withUserModel
pydantic model:
from pydantic import BaseModel
from reforms import BooleanField, EmailField, StringField
from reforms.validators import Length
class UserModel(BaseModel):
first_name: StringField(
label="First Name",
field_id="firstName",
placeholder="John",
validators=[Length(min=5)],
)
last_name: StringField(
label="Last Name",
field_id="lastName",
placeholder="Doe",
validators=[Length(min=5)],
)
email: EmailField(
label="Email",
field_id="email",
placeholder="john.doe@example.com",
)
has_github: BooleanField(label="Has Github account?", field_id="hasGithub") = False
- Then you can create a FastAPI or Starlette application and use this model to generate
form layout and validate data. Reforms has special
from_model
function, which works withDepends
from FastAPI to convert raw form data into pydantic model object. Create a filemain.py
with:
import uvicorn
from fastapi import Depends, FastAPI, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from starlette.status import HTTP_302_FOUND
from reforms import Reforms
from reforms.contrib.fastapi import from_model
from models import UserModel
app = FastAPI()
forms = Reforms(package="reforms")
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
user_form = forms.Form(UserModel)
return templates.TemplateResponse(
"index.html",
{"request": request, "form": user_form},
)
@app.post("/", response_class=RedirectResponse)
async def handle_form(form: from_model(UserModel) = Depends()):
print(form)
return RedirectResponse("/", status_code=HTTP_302_FOUND)
if __name__ == "__main__":
uvicorn.run(app)
import uvicorn
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.requests import Request
from starlette.responses import RedirectResponse
from starlette.templating import Jinja2Templates
from starlette.status import HTTP_302_FOUND
from reforms import Reforms
from models import UserModel
forms = Reforms(package="reforms")
templates = Jinja2Templates(directory="templates")
async def index(request: Request):
user_form = forms.Form(UserModel)
return templates.TemplateResponse(
"index.html",
{"request": request, "form": user_form},
)
async def handle_form(request: Request):
raw_form = await request.form()
form = UserModel(**raw_form)
print(form)
return RedirectResponse("/", status_code=HTTP_302_FOUND)
if __name__ == "__main__":
app = Starlette(
routes=[
Route('/', endpoint=index),
Route('/', endpoint=handle_form, methods=["POST"]),
],
)
uvicorn.run(app)
(This script is complete, it should run "as is")
- As the last coding step, you need to create a template (now reforms supports only jinja2 templates). You can use just form object to render all fields simultaneously or render every field separately (as it mentions in the selected commented line).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example reforms page</title>
</head>
<body>
<form action="/" method="post">
{{ form }}
{#{{ form.first_name }}#}
<br>
<input type="submit" value="Send">
</form>
</body>
</html>
Run it¶
Run the server with:
$ uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
About the command
uvicorn main:app --reload
...
The command uvicorn main:app
refers to:
main
: the filemain.py
(the Python "module").app
: the object created inside ofmain.py
with the lineapp = FastAPI()
.--reload
: make the server restart after code changes. Only do this for development.
or just with:
$ python main.py
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
Send it¶
Open your browser at http://127.0.0.1:8000.
You will see the web form:
Add some information like this and click "Send" button:
Check it¶
Finally, you can see a printed validated model object in your console:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: 127.0.0.1:33612 - "GET / HTTP/1.1" 200 OK
first_name='Roman' last_name='Dukkee' email='example@example.com' has_github=True
INFO: 127.0.0.1:33872 - "POST / HTTP/1.1" 302 Found
INFO: 127.0.0.1:33872 - "GET / HTTP/1.1" 200 OK
Acknowledgments¶
Special thanks to:
-
Sebastián Ramírez and his FastAPI project, some scripts and documentation structure and parts were used from there.
-
Samuel Colvin and his Pydantic project.
License¶
This project is licensed under the terms of the MIT license.