Pydantic-based HTTP forms
Project description
Fodantic
Pydantic-based HTTP forms.
Pydantic is the most widely used data validation library for Python, but it's hard to use it with regular HTTP forms... until now.
Fodantic allow you to quickly wrap your Pydantic models and use them as forms: with support for multiple values, checkboxes, error handling, and integration with your favorite ORM.
A simple example
from fodantic import formable
from pydantic import BaseModel
@formable
class UserModel(BaseModel):
name: str
friends: list[int]
active: bool = True
# This is just an example. Here you would use the
# request POST data of your web framework instead.
# For example, for Flask: `request_data = request.form`
from multidict import MultiDict
request_data = MultiDict([
("name", "John Doe"),
("friends", "2"),
("friends", "3"),
])
# The magic
form = UserModel.as_form(request_data, object=None)
print(form)
#> UserModel.as_form(name='John Doe', friends=[2, 3], active=False)
print(form.fields["name"].value)
#> John Doe
print(form.fields["name"].error)
#> None
print(form.save()) # Can also update the `object` passed as an argument
#> {'name': 'John Doe', 'friends': [2, 3], 'active': False}
Installation
pip install fodantic
Requirements
- Python > 3.10
- Pydantic 2.*
Form Fields Parsing with Nested Notation
Fodantic supports parsing nested form fields using bracket ([]
) notation -- similar to how Ruby on Rails and PHP handle form data.
This allows you to easily create complex nested data structures from flat form submissions.
Nested Object Notation
You can use brackets to define nested objects in your form fields:
<input name="user[name]" value="Alice">
<input name="user[email]" value="alice@example.com">
<input name="user[address][city]" value="New York">
<input name="user[address][zip]" value="10001">
This will be parsed into a nested structure:
{
"user": {
"name": "Alice",
"email": "alice@example.com",
"address": {
"city": "New York",
"zip": "10001"
}
}
}
Array Notation
You can create arrays using numeric indexes or empty brackets:
Indexed Arrays
<input name="contacts[0][name]" value="John">
<input name="contacts[0][phone]" value="555-1234">
<input name="contacts[1][name]" value="Jane">
<input name="contacts[1][phone]" value="555-5678">
This will be parsed into:
{
"contacts": [
{"name": "John", "phone": "555-1234"},
{"name": "Jane", "phone": "555-5678"},
]
}
Array Append (Empty Brackets)
<input name="tags[]" value="important">
<input name="tags[]" value="urgent">
<input name="tags[]" value="follow-up">
This will be parsed into:
{
"tags": ["important", "urgent", "follow-up"]
}
Mixed Structures
You can combine these notations to create complex data structures:
<input name="user[name]" value="Bob">
<input name="user[skills][]" value="Python">
<input name="user[skills][]" value="JavaScript">
<input name="user[projects][0][name]" value="Project A">
<input name="user[projects][0][status]" value="active">
<input name="user[projects][1][name]" value="Project B">
<input name="user[projects][1][status]" value="pending">
This will be parsed into:
{
"user": {
"name": "Bob",
"skills": ["Python", "JavaScript"],
"projects": [
{"name": "Project A", "status": "active"},
{"name": "Project B", "status": "pending"}
]
}
}
Usage with Pydantic Models
This nested notation works seamlessly with Pydantic models, allowing you to map complex form structures to nested models:
from fodantic import formable
from pydantic import BaseModel
from typing import List
class Address(BaseModel):
city: str
zip: str
class Project(BaseModel):
name: str
status: str
@formable
class UserModel(BaseModel):
name: str
skills: List[str] = []
address: Address
projects: List[Project] = []
# Your form data with nested structure
form = UserModel.as_form(request_data)
The parser handles all the complexity of transforming the flat form structure into the nested objects your models expect.
Booleans fields
Boolean fields are treated special because of how browsers handle checkboxes:
- If not checked: the browser doesn't send the field at all, so the missing field will be interpreted as
False
. - If checked: It sends the "value" attribute, but this is optional, so it could send an empty string instead. So any value other than None will be interpreted as
True
.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file fodantic-0.1.tar.gz
.
File metadata
- Download URL: fodantic-0.1.tar.gz
- Upload date:
- Size: 14.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.9
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 |
e1e22d5bf4ca208e370623cb4f24f712c66d1e80b088a1b91d6b95f792f1515f
|
|
MD5 |
1f960c48987dd8ad66d018de164a5207
|
|
BLAKE2b-256 |
83b6d1173ae51ab076cea8566c2151038ba109d89ec595f3dbcb59cefd0ef428
|
File details
Details for the file fodantic-0.1-py3-none-any.whl
.
File metadata
- Download URL: fodantic-0.1-py3-none-any.whl
- Upload date:
- Size: 9.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.9
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 |
01da47dce34434625958034aaccf7f84c00919e42143dcd183c4f21431d7e1b9
|
|
MD5 |
8094322ede257452bbd902fc2b472bbb
|
|
BLAKE2b-256 |
5dce28b53bbbb5bb06f1c59162f19364f34c3a1d1da1c61c4340853a2a10dd96
|