Package: aiohttp-pydantic
Aiohttp pydantic is anaiohttp viewto easily parse and validate request.
You define using the function annotations what your methods for handling HTTP verbs expects and Aiohttp pydantic parses the HTTP request
for you, validates the data, and injects that you want as parameters.Features:Query string, request body, URL path and HTTP headers validation.Open API Specification generation.How to install$pipinstallaiohttp_pydanticExample:fromtypingimportOptionalfromaiohttpimportwebfromaiohttp_pydanticimportPydanticViewfrompydanticimportBaseModel# Use pydantic BaseModel to validate request bodyclassArticleModel(BaseModel):name:strnb_page:Optional[int]# Create your PydanticView and add annotations.classArticleView(PydanticView):asyncdefpost(self,article:ArticleModel):returnweb.json_response({'name':article.name,'number_of_page':article.nb_page})asyncdefget(self,with_comments:bool=False):returnweb.json_response({'with_comments':with_comments})app=web.Application()app.router.add_view('/article',ArticleView)web.run_app(app)$curl-XGEThttp://127.0.0.1:8080/article?with_comments=a[{"in":"query string","loc":["with_comments"],"msg":"Input should be a valid boolean, unable to interpret input","input":"a","type":"bool_parsing"}]$curl-XGEThttp://127.0.0.1:8080/article?with_comments=yes{"with_comments":true}$curl-H"Content-Type: application/json"-XPOSThttp://127.0.0.1:8080/article--data'{}'[{"in":"body","loc":["name"],"msg":"Field required","input":{},"type":"missing"},{"in":"body","loc":["nb_page"],"msg":"Field required","input":{},"type":"missing"}]$curl-H"Content-Type: application/json"-XPOSThttp://127.0.0.1:8080/article--data'{"name": "toto", "nb_page": "3"}'{"name":"toto","number_of_page":3}API:Inject Path ParametersTo declare a path parameter, you must declare your argument as apositional-only parameters:Example:classAccountView(PydanticView):asyncdefget(self,customer_id:str,account_id:str,/):...app=web.Application()app.router.add_get('/customers/{customer_id}/accounts/{account_id}',AccountView)Inject Query String ParametersTo declare a query parameter, you must declare your argument as a simple argument:classAccountView(PydanticView):asyncdefget(self,customer_id:Optional[str]=None):...app=web.Application()app.router.add_get('/customers',AccountView)A query string parameter is generally optional and we do not want to force the user to set it in the URL.
It’s recommended to define a default value. It’s possible to get a multiple value for the same parameter using
the List typefromtypingimportListfrompydanticimportFieldclassAccountView(PydanticView):asyncdefget(self,tags:List[str]=Field(default_factory=list)):...app=web.Application()app.router.add_get('/customers',AccountView)Inject Request BodyTo declare a body parameter, you must declare your argument as a simple argument annotated withpydantic Model.classCustomer(BaseModel):first_name:strlast_name:strclassCustomerView(PydanticView):asyncdefpost(self,customer:Customer):...app=web.Application()app.router.add_view('/customers',CustomerView)Inject HTTP headersTo declare a HTTP headers parameter, you must declare your argument as akeyword-only argument.classCustomerView(PydanticView):asyncdefget(self,*,authorization:str,expire_at:datetime):...app=web.Application()app.router.add_view('/customers',CustomerView)Add route to generate Open Api Specification (OAS)aiohttp_pydantic provides a sub-application to serve a route to generate Open Api Specification
reading annotation in your PydanticView. Useaiohttp_pydantic.oas.setup()to add the sub-applicationfromaiohttpimportwebfromaiohttp_pydanticimportoasapp=web.Application()oas.setup(app)By default, the route to display the Open Api Specification is /oas but you can change it usingurl_prefixparameteroas.setup(app,url_prefix='/spec-api')If you want generate the Open Api Specification from specific aiohttp sub-applications.
on the same route, you must useapps_to_exposeparameter.fromaiohttpimportwebfromaiohttp_pydanticimportoasapp=web.Application()sub_app_1=web.Application()sub_app_2=web.Application()oas.setup(app,apps_to_expose=[sub_app_1,sub_app_2])You can change the title or the version of the generated open api specification usingtitle_specandversion_specparameters:oas.setup(app,title_spec="My application",version_spec="1.2.3")Add annotation to define response contentThe module aiohttp_pydantic.oas.typing provides class to annotate a
response content.For exampler200[List[Pet]]means the server responses with
the status code 200 and the response content is a List of Pet where Pet will be
defined using a pydantic.BaseModelThe docstring of methods will be parsed to fill the descriptions in the
Open Api Specification.fromaiohttp_pydanticimportPydanticViewfromaiohttp_pydantic.oas.typingimportr200,r201,r204,r404classPet(BaseModel):id:intname:strclassError(BaseModel):error:strclassPetCollectionView(PydanticView):asyncdefget(self)->r200[List[Pet]]:"""
        Find all pets

        Tags: pet
        """pets=self.request.app["model"].list_pets()returnweb.json_response([pet.dict()forpetinpets])asyncdefpost(self,pet:Pet)->r201[Pet]:"""
        Add a new pet to the store

        Tags: pet
        Status Codes:
            201: The pet is created
        """self.request.app["model"].add_pet(pet)returnweb.json_response(pet.dict())classPetItemView(PydanticView):asyncdefget(self,id:int,/)->Union[r200[Pet],r404[Error]]:"""
        Find a pet by ID

        Tags: pet
        Status Codes:
            200: Successful operation
            404: Pet not found
        """pet=self.request.app["model"].find_pet(id)returnweb.json_response(pet.dict())asyncdefput(self,id:int,/,pet:Pet)->r200[Pet]:"""
        Update an existing pet

        Tags: pet
        Status Codes:
            200: successful operation
        """self.request.app["model"].update_pet(id,pet)returnweb.json_response(pet.dict())asyncdefdelete(self,id:int,/)->r204:self.request.app["model"].remove_pet(id)returnweb.Response(status=204)Group parametersIf your method has lot of parameters you can group them together inside one or several Groups.fromaiohttp_pydantic.injectorsimportGroupclassPagination(Group):page_num:int=1page_size:int=15classArticleView(PydanticView):asyncdefget(self,page:Pagination):articles=Article.get(page.page_num,page.page_size)...The parameters page_num and page_size are expected in the query string, and
set inside a Pagination object passed as page parameter.The code above is equivalent to:classArticleView(PydanticView):asyncdefget(self,page_num:int=1,page_size:int=15):articles=Article.get(page_num,page_size)...You can add methods or properties to your Group.classPagination(Group):page_num:int=1page_size:int=15@propertydefnum(self):returnself.page_num@propertydefsize(self):returnself.page_sizedefslice(self):returnslice(self.num,self.size)classArticleView(PydanticView):asyncdefget(self,page:Pagination):articles=Article.get(page.num,page.size)...Custom Validation errorYou can redefine the on_validation_error hook in your PydanticViewclassPetView(PydanticView):asyncdefon_validation_error(self,exception:ValidationError,context:str):errors=exception.errors()forerrorinerrors:error["in"]=context# context is "body", "headers", "path" or "query string"error["custom"]="your custom field ..."returnjson_response(data=errors,status=400)DemoHave a look atdemofor a complete examplegitclonehttps://github.com/Maillol/aiohttp-pydantic.gitcdaiohttp-pydanticpipinstall.python-mdemoGo tohttp://127.0.0.1:8080/oasYou can generate the OAS in a json or yaml file using the aiohttp_pydantic.oas command:python-maiohttp_pydantic.oasdemo.main$python3-maiohttp_pydantic.oas--helpusage:__main__.py[-h][-bFILE][-oFILE][-fFORMAT][APP[APP...]]GenerateOpenAPISpecificationpositionalarguments:APPThenameofthemodulecontainingtheasyncio.web.Application.Bydefaultthevariablenamed'app'isloadedbutyoucandefineanothervariablenameendingthenameofmodulewith:charactersandthenameofvariable.Example:my_package.my_module:my_appIfyourasyncio.web.Applicationisreturnedbyafunction,youcanusethesyntax:my_package.my_module:my_app()optionalarguments:-h,--helpshowthishelpmessageandexit-bFILE,--base-oas-fileFILEAfilethatwillbeusedasbasetogenerateOAS-oFILE,--outputFILEFiletowritetheoutput-fFORMAT,--formatFORMATTheoutputformat,canbe'json'or'yaml'(defaultisjson)