{"id":15442003,"url":"https://github.com/jpsca/fodantic","last_synced_at":"2026-01-27T13:09:51.082Z","repository":{"id":230485049,"uuid":"779018712","full_name":"jpsca/fodantic","owner":"jpsca","description":"Pydantic-based HTTP forms","archived":false,"fork":false,"pushed_at":"2025-06-02T21:56:30.000Z","size":507,"stargazers_count":17,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-11-17T01:19:29.169Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jpsca.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"jpsca"}},"created_at":"2024-03-28T21:51:21.000Z","updated_at":"2025-07-30T11:58:02.000Z","dependencies_parsed_at":"2024-03-30T03:29:03.310Z","dependency_job_id":"adb66a8a-314d-41fd-af82-0de8174ca151","html_url":"https://github.com/jpsca/fodantic","commit_stats":null,"previous_names":["jpsca/fodantic"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/jpsca/fodantic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpsca%2Ffodantic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpsca%2Ffodantic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpsca%2Ffodantic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpsca%2Ffodantic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jpsca","download_url":"https://codeload.github.com/jpsca/fodantic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpsca%2Ffodantic/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28813276,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-27T12:25:15.069Z","status":"ssl_error","status_checked_at":"2026-01-27T12:25:05.297Z","response_time":168,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-01T19:24:44.447Z","updated_at":"2026-01-27T13:09:51.062Z","avatar_url":"https://github.com/jpsca.png","language":"Python","funding_links":["https://github.com/sponsors/jpsca"],"categories":["Utilities"],"sub_categories":[],"readme":"# Fodantic\n\n\u003cimg align=\"right\" height=\"200\" src=\"https://raw.githubusercontent.com/jpsca/fodantic/refs/heads/main/fodantic.png\"\u003e\n\nPydantic-based HTTP forms.\n\n[Pydantic](https://docs.pydantic.dev) is the most widely used data validation library for Python, but it's hard to use it with regular HTTP forms... until now.\n\n**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.\n\n## A simple example\n\n```py\nfrom fodantic import formable\nfrom pydantic import BaseModel\n\n@formable\nclass UserModel(BaseModel):\n    name: str\n    friends: list[int]\n    active: bool = True\n\n# This is just an example. Here you would use the\n# request POST data of your web framework instead.\n# For example, for Flask: `request_data = request.form`\nfrom multidict import MultiDict\nrequest_data = MultiDict([\n  (\"name\", \"John Doe\"),\n  (\"friends\", \"2\"),\n  (\"friends\", \"3\"),\n])\n\n# The magic\nform = UserModel.as_form(request_data, object=None)\n\nprint(form)\n#\u003e UserModel.as_form(name='John Doe', friends=[2, 3], active=False)\nprint(form.fields[\"name\"].value)\n#\u003e John Doe\nprint(form.fields[\"name\"].error)\n#\u003e None\nprint(form.save())  # Can also update the `object` passed as an argument\n#\u003e {'name': 'John Doe', 'friends': [2, 3], 'active': False}\n\n```\n\n## Installation\n\n  pip install fodantic\n\n### Requirements\n\n- Python \u003e 3.10\n- Pydantic 2.*\n\n## Form Fields Parsing with Nested Notation\n\nFodantic supports parsing nested form fields using bracket (`[]`) notation -- similar to how Ruby on Rails and PHP handle form data.\nThis allows you to easily create complex nested data structures from flat form submissions.\n\n### Nested Object Notation\n\nYou can use brackets to define nested objects in your form fields:\n\n```html\n\u003cinput name=\"user[name]\" value=\"Alice\"\u003e\n\u003cinput name=\"user[email]\" value=\"alice@example.com\"\u003e\n\u003cinput name=\"user[address][city]\" value=\"New York\"\u003e\n\u003cinput name=\"user[address][zip]\" value=\"10001\"\u003e\n```\n\nThis will be parsed into a nested structure:\n\n```python\n{\n    \"user\": {\n        \"name\": \"Alice\",\n        \"email\": \"alice@example.com\",\n        \"address\": {\n            \"city\": \"New York\",\n            \"zip\": \"10001\"\n        }\n    }\n}\n```\n\n### Array Notation\n\nYou can create arrays using numeric indexes or empty brackets:\n\n#### Indexed Arrays\n\n```html\n\u003cinput name=\"contacts[0][name]\" value=\"John\"\u003e\n\u003cinput name=\"contacts[0][phone]\" value=\"555-1234\"\u003e\n\u003cinput name=\"contacts[1][name]\" value=\"Jane\"\u003e\n\u003cinput name=\"contacts[1][phone]\" value=\"555-5678\"\u003e\n```\n\nThis will be parsed into:\n\n```python\n{\n  \"contacts\": [\n    {\"name\": \"John\", \"phone\": \"555-1234\"},\n    {\"name\": \"Jane\", \"phone\": \"555-5678\"},\n  ]\n}\n```\n\n#### Array Append (Empty Brackets)\n\n```html\n\u003cinput name=\"tags[]\" value=\"important\"\u003e\n\u003cinput name=\"tags[]\" value=\"urgent\"\u003e\n\u003cinput name=\"tags[]\" value=\"follow-up\"\u003e\n```\n\nThis will be parsed into:\n\n```python\n{\n  \"tags\": [\"important\", \"urgent\", \"follow-up\"]\n}\n```\n\n#### Mixed Structures\n\nYou can combine these notations to create complex data structures:\n\n```html\n\u003cinput name=\"user[name]\" value=\"Bob\"\u003e\n\u003cinput name=\"user[skills][]\" value=\"Python\"\u003e\n\u003cinput name=\"user[skills][]\" value=\"JavaScript\"\u003e\n\u003cinput name=\"user[projects][0][name]\" value=\"Project A\"\u003e\n\u003cinput name=\"user[projects][0][status]\" value=\"active\"\u003e\n\u003cinput name=\"user[projects][1][name]\" value=\"Project B\"\u003e\n\u003cinput name=\"user[projects][1][status]\" value=\"pending\"\u003e\n```\n\nThis will be parsed into:\n\n```python\n{\n    \"user\": {\n        \"name\": \"Bob\",\n        \"skills\": [\"Python\", \"JavaScript\"],\n        \"projects\": [\n            {\"name\": \"Project A\", \"status\": \"active\"},\n            {\"name\": \"Project B\", \"status\": \"pending\"}\n        ]\n    }\n}\n```\n\n### Usage with Pydantic Models\n\nThis nested notation works seamlessly with Pydantic models, allowing you to map complex form structures to nested models:\n\n```python\nfrom fodantic import formable\nfrom pydantic import BaseModel\nfrom typing import List\n\nclass Address(BaseModel):\n    city: str\n    zip: str\n\nclass Project(BaseModel):\n    name: str\n    status: str\n\n@formable\nclass UserModel(BaseModel):\n    name: str\n    skills: List[str] = []\n    address: Address\n    projects: List[Project] = []\n\n# Your form data with nested structure\nform = UserModel.as_form(request_data)\n```\n\nThe parser handles all the complexity of transforming the flat form structure into the nested objects your models expect.\n\n## Booleans fields\n\nBoolean fields are treated special because of how browsers handle checkboxes:\n\n- If not checked: the browser doesn't send the field at all, so the missing field will be interpreted as `False`.\n- 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`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjpsca%2Ffodantic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjpsca%2Ffodantic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjpsca%2Ffodantic/lists"}