{"id":26901591,"url":"https://github.com/pallets-eco/flask-pydantic","last_synced_at":"2025-04-08T09:09:45.363Z","repository":{"id":38322357,"uuid":"232153469","full_name":"pallets-eco/flask-pydantic","owner":"pallets-eco","description":"flask extension for integration with the awesome pydantic package","archived":false,"fork":false,"pushed_at":"2024-11-16T01:34:05.000Z","size":125,"stargazers_count":384,"open_issues_count":34,"forks_count":57,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-03-30T09:01:54.424Z","etag":null,"topics":["flask","pydantic","validation"],"latest_commit_sha":null,"homepage":"","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/pallets-eco.png","metadata":{"files":{"readme":"README.md","changelog":"HISTORY.md","contributing":null,"funding":null,"license":"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}},"created_at":"2020-01-06T17:45:59.000Z","updated_at":"2025-03-30T03:41:48.000Z","dependencies_parsed_at":"2023-12-11T05:02:17.123Z","dependency_job_id":"e1251fce-1492-4f76-878a-e8fc4798576e","html_url":"https://github.com/pallets-eco/flask-pydantic","commit_stats":{"total_commits":134,"total_committers":16,"mean_commits":8.375,"dds":"0.20895522388059706","last_synced_commit":"8595fa8b5513a336c9c679829f49ddc20f56377d"},"previous_names":["bauerji/flask_pydantic","pallets-eco/flask-pydantic"],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pallets-eco%2Fflask-pydantic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pallets-eco%2Fflask-pydantic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pallets-eco%2Fflask-pydantic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pallets-eco%2Fflask-pydantic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pallets-eco","download_url":"https://codeload.github.com/pallets-eco/flask-pydantic/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247809964,"owners_count":20999816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["flask","pydantic","validation"],"created_at":"2025-04-01T08:30:45.619Z","updated_at":"2025-04-08T09:09:45.354Z","avatar_url":"https://github.com/pallets-eco.png","language":"Python","funding_links":[],"categories":["Python","Flask Utilities","Third-Party Extensions"],"sub_categories":["Data Validation and Serialization"],"readme":"# Flask-Pydantic\n\n[![PyPI](https://img.shields.io/pypi/v/Flask-Pydantic?color=g)](https://pypi.org/project/Flask-Pydantic/)\n[![License](https://img.shields.io/badge/license-MIT-purple)](https://github.com/bauerji/flask_pydantic/blob/master/LICENSE)\n\nFlask extension for integration of the awesome [pydantic package](https://github.com/samuelcolvin/pydantic) with [Flask](https://palletsprojects.com/p/flask/).\n\n## Pallets Community Ecosystem\n\n\u003e [!IMPORTANT]\\\n\u003e This project is part of the Pallets Community Ecosystem. Pallets is the open\n\u003e source organization that maintains Flask; Pallets-Eco enables community\n\u003e maintenance of Flask extensions. If you are interested in helping maintain\n\u003e this project, please reach out on [the Pallets Discord server][discord].\n\u003e\n\u003e [discord]: https://discord.gg/pallets\n\n## Installation\n\n`python3 -m pip install Flask-Pydantic`\n\n## Basics\n### URL query and body parameters\n\n`validate` decorator validates query, body and form-data request parameters and makes them accessible two ways:\n\n1. [Using `validate` arguments, via flask's `request` variable](#basic-example)\n\n| **parameter type** | **`request` attribute name** |\n|:------------------:|:----------------------------:|\n|       query        |        `query_params`        |\n|        body        |        `body_params`         |\n|        form        |        `form_params`         |\n\n2. [Using the decorated function argument parameters type hints](#using-the-decorated-function-kwargs)\n\n### URL path parameter\n\nIf you use annotated path URL path parameters as follows\n```python\n\n@app.route(\"/users/\u003cuser_id\u003e\", methods=[\"GET\"])\n@validate()\ndef get_user(user_id: str):\n    pass\n```\nflask_pydantic will parse and validate `user_id` variable in the same manner as for body and query parameters.\n\n---\n\n### Additional `validate` arguments\n\n- Success response status code can be modified via `on_success_status` parameter of `validate` decorator.\n- `response_many` parameter set to `True` enables serialization of multiple models (route function should therefore return iterable of models).\n- `request_body_many` parameter set to `False` analogically enables serialization of multiple models inside of the root level of request body. If the request body doesn't contain an array of objects `400` response is returned,\n- `get_json_params` - parameters to be passed to [`flask.Request.get_json`](https://tedboy.github.io/flask/generated/generated/flask.Request.get_json.html) function\n- If validation fails, `400` response is returned with failure explanation.\n\nFor more details see in-code docstring or example app.\n\n## Usage\n\n### Example 1: Query parameters only\n\nSimply use `validate` decorator on route function.\n\n:exclamation: Be aware that `@app.route` decorator must precede `@validate` (i. e. `@validate` must be closer to the function declaration).\n\n```python\nfrom typing import Optional\nfrom flask import Flask, request\nfrom pydantic import BaseModel\n\nfrom flask_pydantic import validate\n\napp = Flask(\"flask_pydantic_app\")\n\nclass QueryModel(BaseModel):\n  age: int\n\nclass ResponseModel(BaseModel):\n  id: int\n  age: int\n  name: str\n  nickname: Optional[str] = None\n\n# Example 1: query parameters only\n@app.route(\"/\", methods=[\"GET\"])\n@validate()\ndef get(query: QueryModel):\n  age = query.age\n  return ResponseModel(\n    age=age,\n    id=0, name=\"abc\", nickname=\"123\"\n    )\n```\n\n\u003ca href=\"example_app/example.py\"\u003e\n  See the full example app here\n\u003c/a\u003e\n\n\n- `age` query parameter is a required `int`\n  - `curl --location --request GET 'http://127.0.0.1:5000/'`\n  - if none is provided the response contains:\n    ```json\n    {\n      \"validation_error\": {\n        \"query_params\": [\n          {\n            \"loc\": [\"age\"],\n            \"msg\": \"field required\",\n            \"type\": \"value_error.missing\"\n          }\n        ]\n      }\n    }\n    ```\n  - for incompatible type (e. g. string `/?age=not_a_number`)\n  - `curl --location --request GET 'http://127.0.0.1:5000/?age=abc'`\n    ```json\n    {\n      \"validation_error\": {\n        \"query_params\": [\n          {\n            \"loc\": [\"age\"],\n            \"msg\": \"value is not a valid integer\",\n            \"type\": \"type_error.integer\"\n          }\n        ]\n      }\n    }\n    ```\n- likewise for body parameters\n- example call with valid parameters:\n  `curl --location --request GET 'http://127.0.0.1:5000/?age=20'`  \n\n-\u003e `{\"id\": 0, \"age\": 20, \"name\": \"abc\", \"nickname\": \"123\"}`\n\n\n### Example 2: URL path parameter\n\n```python\n@app.route(\"/character/\u003ccharacter_id\u003e/\", methods=[\"GET\"])\n@validate()\ndef get_character(character_id: int):\n    characters = [\n        ResponseModel(id=1, age=95, name=\"Geralt\", nickname=\"White Wolf\"),\n        ResponseModel(id=2, age=45, name=\"Triss Merigold\", nickname=\"sorceress\"),\n        ResponseModel(id=3, age=42, name=\"Julian Alfred Pankratz\", nickname=\"Jaskier\"),\n        ResponseModel(id=4, age=101, name=\"Yennefer\", nickname=\"Yenn\"),\n    ]\n    try:\n        return characters[character_id]\n    except IndexError:\n        return {\"error\": \"Not found\"}, 400\n```\n\n\n### Example 3: Request body only\n\n```python\nclass RequestBodyModel(BaseModel):\n  name: str\n  nickname: Optional[str] = None\n\n# Example2: request body only\n@app.route(\"/\", methods=[\"POST\"])\n@validate()\ndef post(body: RequestBodyModel):\n  name = body.name\n  nickname = body.nickname\n  return ResponseModel(\n    name=name, nickname=nickname,id=0, age=1000\n    )\n```\n\n\u003ca href=\"example_app/example.py\"\u003e\n  See the full example app here\n\u003c/a\u003e\n\n### Example 4: BOTH query paramaters and request body\n\n```python\n# Example 3: both query paramters and request body\n@app.route(\"/both\", methods=[\"POST\"])\n@validate()\ndef get_and_post(body: RequestBodyModel, query: QueryModel):\n  name = body.name # From request body\n  nickname = body.nickname # From request body\n  age = query.age # from query parameters\n  return ResponseModel(\n    age=age, name=name, nickname=nickname,\n    id=0\n  )\n```\n\n\u003ca href=\"example_app/example.py\"\u003e\n  See the full example app here\n\u003c/a\u003e\n\n\n### Example 5: Request form-data only\n\n```python\nclass RequestFormDataModel(BaseModel):\n  name: str\n  nickname: Optional[str] = None\n\n# Example2: request body only\n@app.route(\"/\", methods=[\"POST\"])\n@validate()\ndef post(form: RequestFormDataModel):\n  name = form.name\n  nickname = form.nickname\n  return ResponseModel(\n    name=name, nickname=nickname,id=0, age=1000\n    )\n```\n\n\u003ca href=\"example_app/example.py\"\u003e\n  See the full example app here\n\u003c/a\u003e\n\n### Modify response status code\n\nThe default success status code is `200`. It can be modified in two ways\n\n- in return statement\n\n```python\n# necessary imports, app and models definition\n...\n\n@app.route(\"/\", methods=[\"POST\"])\n@validate(body=BodyModel, query=QueryModel)\ndef post():\n    return ResponseModel(\n            id=id_,\n            age=request.query_params.age,\n            name=request.body_params.name,\n            nickname=request.body_params.nickname,\n        ), 201\n```\n\n- in `validate` decorator\n\n```python\n@app.route(\"/\", methods=[\"POST\"])\n@validate(body=BodyModel, query=QueryModel, on_success_status=201)\ndef post():\n    ...\n```\n\nStatus code in case of validation error can be modified using `FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE` flask configuration variable.\n\n### Using the decorated function `kwargs`\n\nInstead of passing `body` and `query` to `validate`, it is possible to directly\ndefined them by using type hinting in the decorated function.\n\n```python\n# necessary imports, app and models definition\n...\n\n@app.route(\"/\", methods=[\"POST\"])\n@validate()\ndef post(body: BodyModel, query: QueryModel):\n    return ResponseModel(\n            id=id_,\n            age=query.age,\n            name=body.name,\n            nickname=body.nickname,\n        )\n```\n\nThis way, the parsed data will be directly available in `body` and `query`.\nFurthermore, your IDE will be able to correctly type them.\n\n### Model aliases\n\nPydantic's [alias feature](https://pydantic-docs.helpmanual.io/usage/model_config/#alias-generator) is natively supported for query and body models.\nTo use aliases in response modify response model\n```python\ndef modify_key(text: str) -\u003e str:\n    # do whatever you want with model keys\n    return text\n\n\nclass MyModel(BaseModel):\n    ...\n    model_config = ConfigDict(\n        alias_generator=modify_key,\n        populate_by_name=True\n    )\n\n```\n\nand set `response_by_alias=True` in `validate` decorator\n\n```python\n@app.route(...)\n@validate(response_by_alias=True)\ndef my_route():\n    ...\n    return MyModel(...)\n```\n\n### Example app\n\nFor more complete examples see [example application](https://github.com/bauerji/flask_pydantic/tree/master/example_app).\n\n### Configuration\n\nThe behaviour can be configured using flask's application config\n`FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE` - response status code after validation error (defaults to `400`)\n\nAdditionally, you can set `FLASK_PYDANTIC_VALIDATION_ERROR_RAISE` to `True` to cause\n`flask_pydantic.ValidationError` to be raised with either `body_params`,\n`form_params`, `path_params`, or `query_params` set as a list of error\ndictionaries. You can use `flask.Flask.register_error_handler` to catch that\nexception and fully customize the output response for a validation error.\n\n## Contributing\n\nFeature requests and pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.\n\n- clone repository\n  ```bash\n  git clone https://github.com/pallets-eco/flask_pydantic.git\n  cd flask_pydantic\n  ```\n- create virtual environment and activate it\n  ```bash\n  python3 -m venv venv\n  source venv/bin/activate\n  ```\n- install development requirements\n  ```bash\n  python3 -m pip install -r requirements/test.txt\n  ```\n- checkout new branch and make your desired changes (don't forget to update tests)\n  ```bash\n  git checkout -b \u003cyour_branch_name\u003e\n  ```\n- make sure your code style is compliant with [Ruff](https://github.com/astral-sh/ruff). Your can check these errors and automatically correct some of them with `ruff check --select I --fix . `\n- run tests and check code format\n  ```bash\n  python3 -m pytest --ruff --ruff-format\n  ```\n- push your changes and create a pull request to master branch\n\n## TODOs:\n\n- header request parameters\n- cookie request parameters\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpallets-eco%2Fflask-pydantic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpallets-eco%2Fflask-pydantic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpallets-eco%2Fflask-pydantic/lists"}