The Field object plays a vital role in Pait.
In addition to get data sources through the Field object, Pait can also implement other feature.
However, this chapter only focuses on parameter verification.
1.Kind of Field
In addition to the Json mentioned above(Introduction), Field also has other class, their names and functions are as follows:
Body: Get the json data of the current request
Cookie: Get the cookie data of the current request (note that the current cookie data will be converted into a Python dictionary, which means that the key of the cookie cannot be repeated. At the same time, when the Field is a cookie, the type is preferably str)
File:Get the file object of the current request, which is consistent with the file object of the web framework
Form:Get the form data of the current request. If there are multiple duplicate Keys, only the first value will be returned
Json: Get the json data of the current request
Header: Get the header data of the current request
Path: Get the path data of the current request, such as /api/{version}/test, will get the version data
Query: Get the data corresponding to the Url parameter of the current request. If there are multiple duplicate keys, only the first value will be returned
MultiForm:Get the form data of the current request, and return the data list corresponding to the Key
MultiQuery:Get the data corresponding to the Url parameter of the current request, and return the data list corresponding to the Key
Field is easy to use, just use Field in the default of <name>:<type>=<default>, using this code as an example.
In order to ensure that the sample code can run smoothly anywhere, the usage of the File field is not demonstrated here.
For specific usage, please refer to the route function corresponding to /api/pait-base-field in the field_route.py file in the sample code of different web frameworks.
The sample code demonstrates how to get the request parameters from the request object through different types of Field and assemble them into a certain format and return them.
Next, use the curl command to a request test:
It can be found from the output results that Pait can accurately obtain the corresponding value from the request object through the Field type.
2.Field feature
From the above example, can see that the url does not carry the email parameter,
but the email in the response value is example@xxx.com.
This is because Pait will assign the default of Field to the variable when the email value cannot be obtained through the request body.
In addition to the default value, Field also has many functions, most of which are derived from pydantic.Field.
2.1.default
Pait gets the default value of the parameter by the default attribute of Field.
When the default attribute of the Field is not null and the request body does not have a corresponding value,
Pait injects the value of default into the corresponding variable.
The following is a simple sample code. Both route functions in the sample code directly return the obtained value demo_value.
The demo has a default value of the string -- 123, while the demo1 has no default value:
After running the code and calling curl, can see that the /api/demo route returns 123 by default when the demo_value parameter is not passed, while the /api/demo1 route throws an error that the demo_value value was not found, as follows.
curl "http://127.0.0.1:8000/api/demo"123curl "http://127.0.0.1:8000/api/demo1"Can not found demo_value value
When the demo value parameter passed is 456, both the /api/demo and /api/demo 1 routes will return 456:
Error handling uses Tip Exception. You can learn about the function of TipException through Exception Tip.
2.2.default_factory
The feature of default_factory is similar to default, except that the value received by default_factory is a function,
which will be executed and inject the return value into the variable when the request hits the route function and Pait cannot find the required value of the variable from the request object.
Sample code is as follows, the default value of the first route function is the current time, the default value of the second route function is uuid, and their return values are generated each time a request is received.
Normally Pait will get data from the request body with the parameter name key,
but some parameter names such as Content-Type are not available in Python.
In this case, can use alias to set an alias for the variable, as shown in the following sample code:
After running the code and calling the curl command,
can find that Pait extracts the value of Content-Type from the header of the request body and assigns it to the content type variable, so the route function can return the value 123 normally:
2.4.GT, GE, LT, LE, and multiple of numeric type checks
gt, ge, lt, le, and multiple_of all belong to pydantic’s numerical type checks.
They are only used for numerical types and have different functions:
gt:A type that is only used for numeric values. It will check whether the value is greater than this value and also add the exclusiveMinimum attribute to the OpenAPI.。
ge:A type that is only used for numeric values. It will check whether the value is greater than or equal this value and also add the exclusiveMaximum attribute to the OpenAPI.。
lt:A type that is only used for numeric values. It will check whether the value is less than this value and also add the exclusiveMinimum attribute to the OpenAPI.。
le:A type that is only used for numeric values. It will check whether the value is less than or equal this value and also add the exclusiveMaximum attribute to the OpenAPI.。
multiple_of:Only used for numbers, it will check whether the number is a multiple of the specified value。
This sample code has only one route function, but it accepts three parameters demo_value1, demo_value2 and demo_value3.
They only receive three parameters that are greater than 1 and less than 10, equal to 1 and a multiple of 3 respectively.
After running the code and calling the curl command, can find that the first request meets the requirements and gets the correct response result.
However, the values of the demo_value 1, demo_value 2 and demo_value 3 parameters of the second, third and fourth requests are not within the required range, so Pait will generate error message by Pydantic.Validation Error, it can be easily seen that the three parameters do not meet the restrictions set by the route function:
➜~curl"http://127.0.0.1:8000/api/demo?demo_value1=2&demo_value2=1&demo_value3=3"{"data":[2,1,3]}➜~curl"http://127.0.0.1:8000/api/demo?demo_value1=11&demo_value2=1&demo_value3=3"{"data":[{"ctx":{"limit_value":10},
"loc":["query","demo_value1"],
"msg":"ensure this value is less than 10",
"type":"value_error.number.not_lt"}]}➜~curl"http://127.0.0.1:8000/api/demo?demo_value1=2&demo_value2=2&demo_value3=3"{"data":[{"ctx":{"limit_value":1},
"loc":["query","demo_value2"],
"msg":"ensure this value is less than or equal to 1",
"type":"value_error.number.not_le"}]}➜~curl"http://127.0.0.1:8000/api/demo?demo_value1=2&demo_value2=1&demo_value3=4"{"data":[{"ctx":{"multiple_of":3},
"loc":["query","demo_value3"],
"msg":"ensure this value is a multiple of 3",
"type":"value_error.number.not_multiple"}]}
2.5.Sequence verification: min_items,max_items
min items and max items both belong to the Sequence type check of pydantic and are only used for the Sequence type. Their functions are different:
min_items:Only used for Sequence type, it will check whether Sequence length is greater than or equal to the specified value.。
max_items: Only used for Sequence type, it will check whether Sequence length is less than or equal to the specified value.
Note
If using Pydantic version greater than 2.0.0, please use min_length and max_length instead of min_items and max_items.
Sample code is as follows,
the route function through the field.MultiQuery from the request Url to obtain the parameter demo_value of the array and return to the caller,
where the length of the array is limited to between 1 and 2:
As in 2.4, by calling the curl command can find that legal parameters will be allowed, and illegal parameters will throw an error:
➜~curl"http://127.0.0.1:8000/api/demo?demo_value=1"{"data":[1]}➜~curl"http://127.0.0.1:8000/api/demo?demo_value=1&demo_value=2"{"data":[1,2]}➜~curl"http://127.0.0.1:8000/api/demo?demo_value=1&demo_value=2&demo_value=3"{"data":[{"loc":["demo_value"],
"msg":"ensure this value has at most 2 items",
"type":"value_error.list.max_items",
"ctx":{"limit_value":2}}]}
min_length, max_length and regex are all part of pydantic's string type checking,
which is only used for string types and they serve different purposes:
min_length:Only used for string type, it will check whether the length of the string is greater than or equal to the specified value.
max_length:Only used for string type, it will check whether the length of the string is less than or equal to the specified value.
regex:Only used for string types, it will check whether the string conforms to the regular expression.
Note
If using Pydantic version greater than 2.0.0, please use pattern instead of regex.
The sample code is as follows, the route function needs to obtain a value with a length of 6 and starting with the English letter u from the Url:
Run the code and use curl to make three requests.
It can be seen from the results that the result of the first request is normal, the result of the second request does not meet the regular expression and the result length of the third request does not meet the requirements:
➜~curl"http://127.0.0.1:8000/api/demo?demo_value=u66666"{"data":"u66666"}➜~curl"http://127.0.0.1:8000/api/demo?demo_value=666666"{"data":[{"loc":["demo_value"],"msg":"string does not match regex \"^u\"","type":"value_error.str.regex","ctx":{"pattern":"^u"}}]}➜~curl"http://127.0.0.1:8000/api/demo?demo_value=1"{"data":[{"loc":["demo_value"],"msg":"ensure this value has at least 6 characters","type":"value_error.any_str.min_length","ctx":{"limit_value":6}}]}
2.7.raw_return
Sample code is as follows,
the route function requires two values, the first value for the entire client passed the Json parameters
and the second value for the client passed the Json parameters in the Key for a value:
Under normal circumstances, if there is no data required by Pait in the request object,
Pait will throw a Not Found Value Exception exception.
In addition, Pait also supports developers to customize exception handling through not_value_exception_func.
For example, the route function in the code below has two variables.
The first variable demo_value1 does not have any Field attributes set.
The second variable demo_value2 sets the not_value_exception_func attribute to lambda param: RuntimeError(f"not found {param.name} data"):
fromflaskimportFlask,Response,jsonifyfrompydanticimportValidationErrorfrompaitimportfieldfrompait.app.flaskimportpaitfrompait.exceptionsimportTipExceptiondefapi_exception(exc:Exception)->Response:ifisinstance(exc,TipException):exc=exc.excifisinstance(exc,ValidationError):returnjsonify({"data":exc.errors()})returnjsonify({"data":str(exc)})@pait()defdemo(demo_value1:str=field.Query.i(),demo_value2:str=field.Query.i(not_value_exception_func=lambdaparam:RuntimeError(f"not found {param.name} data")),)->dict:return{"data":{"demo_value1":demo_value1,"demo_value2":demo_value2}}app=Flask("demo")app.add_url_rule("/api/demo",view_func=demo,methods=["GET"])app.errorhandler(Exception)(api_exception)if__name__=="__main__":app.run(port=8000)
frompydanticimportValidationErrorfromstarlette.applicationsimportStarlettefromstarlette.requestsimportRequestfromstarlette.responsesimportJSONResponsefromstarlette.routingimportRoutefrompaitimportfieldfrompait.app.starletteimportpaitfrompait.exceptionsimportTipExceptionasyncdefapi_exception(request:Request,exc:Exception)->JSONResponse:ifisinstance(exc,TipException):exc=exc.excifisinstance(exc,ValidationError):returnJSONResponse({"data":exc.errors()})returnJSONResponse({"data":str(exc)})@pait()asyncdefdemo(demo_value1:str=field.Query.i(),demo_value2:str=field.Query.i(not_value_exception_func=lambdaparam:RuntimeError(f"not found {param.name} data")),)->JSONResponse:returnJSONResponse({"data":{"demo_value1":demo_value1,"demo_value2":demo_value2}})app=Starlette(routes=[Route("/api/demo",demo,methods=["GET"]),])app.add_exception_handler(Exception,api_exception)if__name__=="__main__":importuvicornuvicorn.run(app)
frompydanticimportValidationErrorfromsanicimportHTTPResponse,Request,Sanic,jsonfrompaitimportfieldfrompait.app.sanicimportpaitfrompait.exceptionsimportTipExceptionasyncdefapi_exception(request:Request,exc:Exception)->HTTPResponse:ifisinstance(exc,TipException):exc=exc.excifisinstance(exc,ValidationError):returnjson({"data":exc.errors()})returnjson({"data":str(exc)})@pait()asyncdefdemo(demo_value1:str=field.Query.i(),demo_value2:str=field.Query.i(not_value_exception_func=lambdaparam:RuntimeError(f"not found {param.name} data")),)->HTTPResponse:returnjson({"data":{"demo_value1":demo_value1,"demo_value2":demo_value2}})app=Sanic("demo")app.add_route(demo,"/api/demo",methods={"GET"})app.exception(Exception)(api_exception)if__name__=="__main__":importuvicornuvicorn.run(app)
frompydanticimportValidationErrorfromtornado.ioloopimportIOLoopfromtornado.webimportApplication,RequestHandlerfrompaitimportfieldfrompait.app.tornadoimportpaitfrompait.exceptionsimportTipExceptionfrompait.openapi.doc_routeimportAddDocRouteclass_Handler(RequestHandler):def_handle_request_exception(self,exc:BaseException)->None:ifisinstance(exc,TipException):exc=exc.excifisinstance(exc,ValidationError):self.write({"data":exc.errors()})else:self.write({"data":str(exc)})self.finish()classDemoHandler(_Handler):@pait()asyncdefget(self,demo_value1:str=field.Query.i(),demo_value2:str=field.Query.i(not_value_exception_func=lambdaparam:RuntimeError(f"not found {param.name} data")),)->None:self.write({"data":{"demo_value1":demo_value1,"demo_value2":demo_value2}})app:Application=Application([(r"/api/demo",DemoHandler)])AddDocRoute(app)if__name__=="__main__":app.listen(8000)IOLoop.instance().start()
Then run the code and execute the following curl command in the terminal:
curl "http://127.0.0.1:8000/api/demo?demo_value1=1&demo_value2=2"{"data": {"demo_value1": "1", "demo_value2": "2"}}curl "http://127.0.0.1:8000/api/demo?demo_value2=2"{"data": "Can not found demo_value1 value"}curl "http://127.0.0.1:8000/api/demo?demo_value1=1"{"data":"not found demo_value2 data"}
Through the output results, can see that the responses to the missing value of demo_value1 and the missing value of demo_value2 are different.
The missing value exception message of demo_value2 is thrown by lambda param: RuntimeError(f"not found {param.name} data").
2.8.Other Feature
In addition to the above feature, Pait also has other feature, which can be found in the corresponding module documentation:
Example values for documentation, mock requests and responses. values support variables and callable functions such as datetime.datetim.now, it is recommended to use faker used together.