%global _empty_manifest_terminate_build 0 Name: python-Flask-Pydantic Version: 0.11.0 Release: 1 Summary: Flask extension for integration with Pydantic library License: MIT URL: https://github.com/bauerji/flask_pydantic.git Source0: https://mirrors.nju.edu.cn/pypi/web/packages/a8/f4/34f688e8a15c1efc7caede3cb9f81f9824e2daccd4b41436a28b2cb5c518/Flask-Pydantic-0.11.0.tar.gz BuildArch: noarch Requires: python3-Flask Requires: python3-pydantic %description ### Additional `validate` arguments - Success response status code can be modified via `on_success_status` parameter of `validate` decorator. - `response_many` parameter set to `True` enables serialization of multiple models (route function should therefore return iterable of models). - `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, - `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 - If validation fails, `400` response is returned with failure explanation. For more details see in-code docstring or example app. ## Usage ### Example 1: Query parameters only Simply use `validate` decorator on route function. ```python from typing import Optional from flask import Flask, request from pydantic import BaseModel from flask_pydantic import validate app = Flask("flask_pydantic_app") class QueryModel(BaseModel): age: int class ResponseModel(BaseModel): id: int age: int name: str nickname: Optional[str] # Example 1: query parameters only @app.route("/", methods=["GET"]) @validate() def get(query: QueryModel): age = query.age return ResponseModel( age=age, id=0, name="abc", nickname="123" ) ``` See the full example app here - `age` query parameter is a required `int` - `curl --location --request GET 'http://127.0.0.1:5000/'` - if none is provided the response contains: ```json { "validation_error": { "query_params": [ { "loc": ["age"], "msg": "field required", "type": "value_error.missing" } ] } } ``` - for incompatible type (e. g. string `/?age=not_a_number`) - `curl --location --request GET 'http://127.0.0.1:5000/?age=abc'` ```json { "validation_error": { "query_params": [ { "loc": ["age"], "msg": "value is not a valid integer", "type": "type_error.integer" } ] } } ``` - likewise for body parameters - example call with valid parameters: `curl --location --request GET 'http://127.0.0.1:5000/?age=20'` -> `{"id": 0, "age": 20, "name": "abc", "nickname": "123"}` ### Example 2: URL path parameter ```python @app.route("/character//", methods=["GET"]) @validate() def get_character(character_id: int): characters = [ ResponseModel(id=1, age=95, name="Geralt", nickname="White Wolf"), ResponseModel(id=2, age=45, name="Triss Merigold", nickname="sorceress"), ResponseModel(id=3, age=42, name="Julian Alfred Pankratz", nickname="Jaskier"), ResponseModel(id=4, age=101, name="Yennefer", nickname="Yenn"), ] try: return characters[character_id] except IndexError: return {"error": "Not found"}, 400 ``` ### Example 3: Request body only ```python class RequestBodyModel(BaseModel): name: str nickname: Optional[str] # Example2: request body only @app.route("/", methods=["POST"]) @validate() def post(body: RequestBodyModel): name = body.name nickname = body.nickname return ResponseModel( name=name, nickname=nickname,id=0, age=1000 ) ``` See the full example app here ### Example 4: BOTH query paramaters and request body ```python # Example 3: both query paramters and request body @app.route("/both", methods=["POST"]) @validate() def get_and_post(body: RequestBodyModel,query: QueryModel): name = body.name # From request body nickname = body.nickname # From request body age = query.age # from query parameters return ResponseModel( age=age, name=name, nickname=nickname, id=0 ) ``` See the full example app here ### Example 5: Request form-data only ```python class RequestFormDataModel(BaseModel): name: str nickname: Optional[str] # Example2: request body only @app.route("/", methods=["POST"]) @validate() def post(form: RequestFormDataModel): name = form.name nickname = form.nickname return ResponseModel( name=name, nickname=nickname,id=0, age=1000 ) ``` See the full example app here ### Modify response status code The default success status code is `200`. It can be modified in two ways - in return statement ```python # necessary imports, app and models definition @app.route("/", methods=["POST"]) @validate(body=BodyModel, query=QueryModel) def post(): return ResponseModel( id=id_, age=request.query_params.age, name=request.body_params.name, nickname=request.body_params.nickname, ), 201 ``` - in `validate` decorator ```python @app.route("/", methods=["POST"]) @validate(body=BodyModel, query=QueryModel, on_success_status=201) def post(): ``` Status code in case of validation error can be modified using `FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE` flask configuration variable. ### Using the decorated function `kwargs` Instead of passing `body` and `query` to `validate`, it is possible to directly defined them by using type hinting in the decorated function. ```python # necessary imports, app and models definition @app.route("/", methods=["POST"]) @validate() def post(body: BodyModel, query: QueryModel): return ResponseModel( id=id_, age=query.age, name=body.name, nickname=body.nickname, ) ``` This way, the parsed data will be directly available in `body` and `query`. Furthermore, your IDE will be able to correctly type them. ### Model aliases Pydantic's [alias feature](https://pydantic-docs.helpmanual.io/usage/model_config/#alias-generator) is natively supported for query and body models. To use aliases in response modify response model ```python def modify_key(text: str) -> str: # do whatever you want with model keys return text class MyModel(BaseModel): class Config: alias_generator = modify_key allow_population_by_field_name = True ``` and set `response_by_alias=True` in `validate` decorator ``` @app.route(...) @validate(response_by_alias=True) def my_route(): return MyModel(...) ``` ### Example app For more complete examples see [example application](https://github.com/bauerji/flask_pydantic/tree/master/example_app). ### Configuration The behaviour can be configured using flask's application config `FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE` - response status code after validation error (defaults to `400`) Additionally, you can set `FLASK_PYDANTIC_VALIDATION_ERROR_RAISE` to `True` to cause `flask_pydantic.ValidationError` to be raised with either `body_params`, `form_params`, `path_params`, or `query_params` set as a list of error dictionaries. You can use `flask.Flask.register_error_handler` to catch that exception and fully customize the output response for a validation error. ## Contributing Feature requests and pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. - clone repository ```bash git clone https://github.com/bauerji/flask_pydantic.git cd flask_pydantic ``` - create virtual environment and activate it ```bash python3 -m venv venv source venv/bin/activate ``` - install development requirements ```bash python3 -m pip install -r requirements/test.pip ``` - checkout new branch and make your desired changes (don't forget to update tests) ```bash git checkout -b ``` - run tests ```bash python3 -m pytest ``` - if tests fails on Black tests, make sure You have your code compliant with style of [Black formatter](https://github.com/psf/black) - push your changes and create a pull request to master branch ## TODOs: - header request parameters - cookie request parameters %package -n python3-Flask-Pydantic Summary: Flask extension for integration with Pydantic library Provides: python-Flask-Pydantic BuildRequires: python3-devel BuildRequires: python3-setuptools BuildRequires: python3-pip %description -n python3-Flask-Pydantic ### Additional `validate` arguments - Success response status code can be modified via `on_success_status` parameter of `validate` decorator. - `response_many` parameter set to `True` enables serialization of multiple models (route function should therefore return iterable of models). - `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, - `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 - If validation fails, `400` response is returned with failure explanation. For more details see in-code docstring or example app. ## Usage ### Example 1: Query parameters only Simply use `validate` decorator on route function. ```python from typing import Optional from flask import Flask, request from pydantic import BaseModel from flask_pydantic import validate app = Flask("flask_pydantic_app") class QueryModel(BaseModel): age: int class ResponseModel(BaseModel): id: int age: int name: str nickname: Optional[str] # Example 1: query parameters only @app.route("/", methods=["GET"]) @validate() def get(query: QueryModel): age = query.age return ResponseModel( age=age, id=0, name="abc", nickname="123" ) ``` See the full example app here - `age` query parameter is a required `int` - `curl --location --request GET 'http://127.0.0.1:5000/'` - if none is provided the response contains: ```json { "validation_error": { "query_params": [ { "loc": ["age"], "msg": "field required", "type": "value_error.missing" } ] } } ``` - for incompatible type (e. g. string `/?age=not_a_number`) - `curl --location --request GET 'http://127.0.0.1:5000/?age=abc'` ```json { "validation_error": { "query_params": [ { "loc": ["age"], "msg": "value is not a valid integer", "type": "type_error.integer" } ] } } ``` - likewise for body parameters - example call with valid parameters: `curl --location --request GET 'http://127.0.0.1:5000/?age=20'` -> `{"id": 0, "age": 20, "name": "abc", "nickname": "123"}` ### Example 2: URL path parameter ```python @app.route("/character//", methods=["GET"]) @validate() def get_character(character_id: int): characters = [ ResponseModel(id=1, age=95, name="Geralt", nickname="White Wolf"), ResponseModel(id=2, age=45, name="Triss Merigold", nickname="sorceress"), ResponseModel(id=3, age=42, name="Julian Alfred Pankratz", nickname="Jaskier"), ResponseModel(id=4, age=101, name="Yennefer", nickname="Yenn"), ] try: return characters[character_id] except IndexError: return {"error": "Not found"}, 400 ``` ### Example 3: Request body only ```python class RequestBodyModel(BaseModel): name: str nickname: Optional[str] # Example2: request body only @app.route("/", methods=["POST"]) @validate() def post(body: RequestBodyModel): name = body.name nickname = body.nickname return ResponseModel( name=name, nickname=nickname,id=0, age=1000 ) ``` See the full example app here ### Example 4: BOTH query paramaters and request body ```python # Example 3: both query paramters and request body @app.route("/both", methods=["POST"]) @validate() def get_and_post(body: RequestBodyModel,query: QueryModel): name = body.name # From request body nickname = body.nickname # From request body age = query.age # from query parameters return ResponseModel( age=age, name=name, nickname=nickname, id=0 ) ``` See the full example app here ### Example 5: Request form-data only ```python class RequestFormDataModel(BaseModel): name: str nickname: Optional[str] # Example2: request body only @app.route("/", methods=["POST"]) @validate() def post(form: RequestFormDataModel): name = form.name nickname = form.nickname return ResponseModel( name=name, nickname=nickname,id=0, age=1000 ) ``` See the full example app here ### Modify response status code The default success status code is `200`. It can be modified in two ways - in return statement ```python # necessary imports, app and models definition @app.route("/", methods=["POST"]) @validate(body=BodyModel, query=QueryModel) def post(): return ResponseModel( id=id_, age=request.query_params.age, name=request.body_params.name, nickname=request.body_params.nickname, ), 201 ``` - in `validate` decorator ```python @app.route("/", methods=["POST"]) @validate(body=BodyModel, query=QueryModel, on_success_status=201) def post(): ``` Status code in case of validation error can be modified using `FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE` flask configuration variable. ### Using the decorated function `kwargs` Instead of passing `body` and `query` to `validate`, it is possible to directly defined them by using type hinting in the decorated function. ```python # necessary imports, app and models definition @app.route("/", methods=["POST"]) @validate() def post(body: BodyModel, query: QueryModel): return ResponseModel( id=id_, age=query.age, name=body.name, nickname=body.nickname, ) ``` This way, the parsed data will be directly available in `body` and `query`. Furthermore, your IDE will be able to correctly type them. ### Model aliases Pydantic's [alias feature](https://pydantic-docs.helpmanual.io/usage/model_config/#alias-generator) is natively supported for query and body models. To use aliases in response modify response model ```python def modify_key(text: str) -> str: # do whatever you want with model keys return text class MyModel(BaseModel): class Config: alias_generator = modify_key allow_population_by_field_name = True ``` and set `response_by_alias=True` in `validate` decorator ``` @app.route(...) @validate(response_by_alias=True) def my_route(): return MyModel(...) ``` ### Example app For more complete examples see [example application](https://github.com/bauerji/flask_pydantic/tree/master/example_app). ### Configuration The behaviour can be configured using flask's application config `FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE` - response status code after validation error (defaults to `400`) Additionally, you can set `FLASK_PYDANTIC_VALIDATION_ERROR_RAISE` to `True` to cause `flask_pydantic.ValidationError` to be raised with either `body_params`, `form_params`, `path_params`, or `query_params` set as a list of error dictionaries. You can use `flask.Flask.register_error_handler` to catch that exception and fully customize the output response for a validation error. ## Contributing Feature requests and pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. - clone repository ```bash git clone https://github.com/bauerji/flask_pydantic.git cd flask_pydantic ``` - create virtual environment and activate it ```bash python3 -m venv venv source venv/bin/activate ``` - install development requirements ```bash python3 -m pip install -r requirements/test.pip ``` - checkout new branch and make your desired changes (don't forget to update tests) ```bash git checkout -b ``` - run tests ```bash python3 -m pytest ``` - if tests fails on Black tests, make sure You have your code compliant with style of [Black formatter](https://github.com/psf/black) - push your changes and create a pull request to master branch ## TODOs: - header request parameters - cookie request parameters %package help Summary: Development documents and examples for Flask-Pydantic Provides: python3-Flask-Pydantic-doc %description help ### Additional `validate` arguments - Success response status code can be modified via `on_success_status` parameter of `validate` decorator. - `response_many` parameter set to `True` enables serialization of multiple models (route function should therefore return iterable of models). - `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, - `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 - If validation fails, `400` response is returned with failure explanation. For more details see in-code docstring or example app. ## Usage ### Example 1: Query parameters only Simply use `validate` decorator on route function. ```python from typing import Optional from flask import Flask, request from pydantic import BaseModel from flask_pydantic import validate app = Flask("flask_pydantic_app") class QueryModel(BaseModel): age: int class ResponseModel(BaseModel): id: int age: int name: str nickname: Optional[str] # Example 1: query parameters only @app.route("/", methods=["GET"]) @validate() def get(query: QueryModel): age = query.age return ResponseModel( age=age, id=0, name="abc", nickname="123" ) ``` See the full example app here - `age` query parameter is a required `int` - `curl --location --request GET 'http://127.0.0.1:5000/'` - if none is provided the response contains: ```json { "validation_error": { "query_params": [ { "loc": ["age"], "msg": "field required", "type": "value_error.missing" } ] } } ``` - for incompatible type (e. g. string `/?age=not_a_number`) - `curl --location --request GET 'http://127.0.0.1:5000/?age=abc'` ```json { "validation_error": { "query_params": [ { "loc": ["age"], "msg": "value is not a valid integer", "type": "type_error.integer" } ] } } ``` - likewise for body parameters - example call with valid parameters: `curl --location --request GET 'http://127.0.0.1:5000/?age=20'` -> `{"id": 0, "age": 20, "name": "abc", "nickname": "123"}` ### Example 2: URL path parameter ```python @app.route("/character//", methods=["GET"]) @validate() def get_character(character_id: int): characters = [ ResponseModel(id=1, age=95, name="Geralt", nickname="White Wolf"), ResponseModel(id=2, age=45, name="Triss Merigold", nickname="sorceress"), ResponseModel(id=3, age=42, name="Julian Alfred Pankratz", nickname="Jaskier"), ResponseModel(id=4, age=101, name="Yennefer", nickname="Yenn"), ] try: return characters[character_id] except IndexError: return {"error": "Not found"}, 400 ``` ### Example 3: Request body only ```python class RequestBodyModel(BaseModel): name: str nickname: Optional[str] # Example2: request body only @app.route("/", methods=["POST"]) @validate() def post(body: RequestBodyModel): name = body.name nickname = body.nickname return ResponseModel( name=name, nickname=nickname,id=0, age=1000 ) ``` See the full example app here ### Example 4: BOTH query paramaters and request body ```python # Example 3: both query paramters and request body @app.route("/both", methods=["POST"]) @validate() def get_and_post(body: RequestBodyModel,query: QueryModel): name = body.name # From request body nickname = body.nickname # From request body age = query.age # from query parameters return ResponseModel( age=age, name=name, nickname=nickname, id=0 ) ``` See the full example app here ### Example 5: Request form-data only ```python class RequestFormDataModel(BaseModel): name: str nickname: Optional[str] # Example2: request body only @app.route("/", methods=["POST"]) @validate() def post(form: RequestFormDataModel): name = form.name nickname = form.nickname return ResponseModel( name=name, nickname=nickname,id=0, age=1000 ) ``` See the full example app here ### Modify response status code The default success status code is `200`. It can be modified in two ways - in return statement ```python # necessary imports, app and models definition @app.route("/", methods=["POST"]) @validate(body=BodyModel, query=QueryModel) def post(): return ResponseModel( id=id_, age=request.query_params.age, name=request.body_params.name, nickname=request.body_params.nickname, ), 201 ``` - in `validate` decorator ```python @app.route("/", methods=["POST"]) @validate(body=BodyModel, query=QueryModel, on_success_status=201) def post(): ``` Status code in case of validation error can be modified using `FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE` flask configuration variable. ### Using the decorated function `kwargs` Instead of passing `body` and `query` to `validate`, it is possible to directly defined them by using type hinting in the decorated function. ```python # necessary imports, app and models definition @app.route("/", methods=["POST"]) @validate() def post(body: BodyModel, query: QueryModel): return ResponseModel( id=id_, age=query.age, name=body.name, nickname=body.nickname, ) ``` This way, the parsed data will be directly available in `body` and `query`. Furthermore, your IDE will be able to correctly type them. ### Model aliases Pydantic's [alias feature](https://pydantic-docs.helpmanual.io/usage/model_config/#alias-generator) is natively supported for query and body models. To use aliases in response modify response model ```python def modify_key(text: str) -> str: # do whatever you want with model keys return text class MyModel(BaseModel): class Config: alias_generator = modify_key allow_population_by_field_name = True ``` and set `response_by_alias=True` in `validate` decorator ``` @app.route(...) @validate(response_by_alias=True) def my_route(): return MyModel(...) ``` ### Example app For more complete examples see [example application](https://github.com/bauerji/flask_pydantic/tree/master/example_app). ### Configuration The behaviour can be configured using flask's application config `FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE` - response status code after validation error (defaults to `400`) Additionally, you can set `FLASK_PYDANTIC_VALIDATION_ERROR_RAISE` to `True` to cause `flask_pydantic.ValidationError` to be raised with either `body_params`, `form_params`, `path_params`, or `query_params` set as a list of error dictionaries. You can use `flask.Flask.register_error_handler` to catch that exception and fully customize the output response for a validation error. ## Contributing Feature requests and pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. - clone repository ```bash git clone https://github.com/bauerji/flask_pydantic.git cd flask_pydantic ``` - create virtual environment and activate it ```bash python3 -m venv venv source venv/bin/activate ``` - install development requirements ```bash python3 -m pip install -r requirements/test.pip ``` - checkout new branch and make your desired changes (don't forget to update tests) ```bash git checkout -b ``` - run tests ```bash python3 -m pytest ``` - if tests fails on Black tests, make sure You have your code compliant with style of [Black formatter](https://github.com/psf/black) - push your changes and create a pull request to master branch ## TODOs: - header request parameters - cookie request parameters %prep %autosetup -n Flask-Pydantic-0.11.0 %build %py3_build %install %py3_install install -d -m755 %{buildroot}/%{_pkgdocdir} if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi pushd %{buildroot} if [ -d usr/lib ]; then find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/lib64 ]; then find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/bin ]; then find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/sbin ]; then find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst fi touch doclist.lst if [ -d usr/share/man ]; then find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst fi popd mv %{buildroot}/filelist.lst . mv %{buildroot}/doclist.lst . %files -n python3-Flask-Pydantic -f filelist.lst %dir %{python3_sitelib}/* %files help -f doclist.lst %{_docdir}/* %changelog * Tue Apr 11 2023 Python_Bot - 0.11.0-1 - Package Spec generated