How to use Type
The type of the variable - Type
is used to indicate the final type of the variable value.
By customizing Type
, can expand the verification rules of the variable, as shown in the following example code:
from pait.app.any import pait
from pait import field
@pait ()
def demo (
a : str = field . Body . i (),
b : int = field . Body . i (),
) -> dict :
return { "a" : a , "b" : b }
When the code is run, Pait
will internally convert the function signature into the following Pydantic.BaseModel
:
from pydantic import BaseModel , Field
class Demo ( BaseModel ):
a : str = Field ()
b : int = Field ()
Therefore, the Type
in the route function can become very flexible.
Can use Type
like Pydantic Field Types .
It is also possible to use some of the features of Pait
by writing a Type
that conforms to the Pait
specification.
1.The value of Type is Pydantic.BaseModel
After a period of development, will find that some route function parameters can be reused. In this case, can use the scheme where the value of Type
is Pydantic.BaseModel
to convert the parameters of the route function into pydantic.Basemodel
.
The following sample code, in the first highlighted code there is a class called DemoModel
, it has uid
, name
and age
three attributes.
In addition to this, there are two different route functions demo
and demo1
in the code.
The demo
takes all the values from the Url Path and passes them to the DemoModel
for validation before returning the data generated by the .dict
method.
While demo1
is very similar to the route function demo
, but the source of the data is changed from Url Path to Json Body.
Flask Starlette Sanic Tornado
docs_source_code/introduction/how_to_use_type/flask_with_model_demo.py from flask import Flask , Response , jsonify
from pydantic import BaseModel , Field
from pait import _pydanitc_adapter , field
from pait.app.flask import pait
if _pydanitc_adapter . is_v1 :
class DemoModel ( BaseModel ):
uid : str = Field ( max_length = 6 , min_length = 6 , regex = "^u" )
name : str = Field ( min_length = 4 , max_length = 10 )
age : int = Field ( ge = 0 , le = 100 )
else :
class DemoModel ( BaseModel ): # type: ignore
uid : str = Field ( max_length = 6 , min_length = 6 , pattern = "^u" )
name : str = Field ( min_length = 4 , max_length = 10 )
age : int = Field ( ge = 0 , le = 100 )
@pait ()
def demo ( demo_model : DemoModel = field . Query . i ( raw_return = True )) -> Response :
return jsonify ( demo_model . dict ())
@pait ()
def demo1 ( demo_model : DemoModel = field . Json . i ( raw_return = True )) -> Response :
return jsonify ( demo_model . dict ())
app = Flask ( "demo" )
app . add_url_rule ( "/api/demo" , "demo" , demo , methods = [ "GET" ])
app . add_url_rule ( "/api/demo1" , "demo1" , demo1 , methods = [ "POST" ])
if __name__ == "__main__" :
app . run ( port = 8000 )
docs_source_code/introduction/how_to_use_type/starlette_with_model_demo.py from pydantic import BaseModel , Field
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from pait import _pydanitc_adapter , field
from pait.app.starlette import pait
if _pydanitc_adapter . is_v1 :
class DemoModel ( BaseModel ):
uid : str = Field ( max_length = 6 , min_length = 6 , regex = "^u" )
name : str = Field ( min_length = 4 , max_length = 10 )
age : int = Field ( ge = 0 , le = 100 )
else :
class DemoModel ( BaseModel ): # type: ignore
uid : str = Field ( max_length = 6 , min_length = 6 , pattern = "^u" )
name : str = Field ( min_length = 4 , max_length = 10 )
age : int = Field ( ge = 0 , le = 100 )
@pait ()
async def demo ( demo_model : DemoModel = field . Query . i ( raw_return = True )) -> JSONResponse :
return JSONResponse ( demo_model . dict ())
@pait ()
async def demo1 ( demo_model : DemoModel = field . Json . i ( raw_return = True )) -> JSONResponse :
return JSONResponse ( demo_model . dict ())
app = Starlette ( routes = [ Route ( "/api/demo" , demo , methods = [ "GET" ]), Route ( "/api/demo1" , demo1 , methods = [ "POST" ])])
if __name__ == "__main__" :
import uvicorn
uvicorn . run ( app )
docs_source_code/introduction/how_to_use_type/sanic_with_model_demo.py from pydantic import BaseModel , Field
from sanic import Sanic , json , response
from pait import _pydanitc_adapter , field
from pait.app.sanic import pait
if _pydanitc_adapter . is_v1 :
class DemoModel ( BaseModel ):
uid : str = Field ( max_length = 6 , min_length = 6 , regex = "^u" )
name : str = Field ( min_length = 4 , max_length = 10 )
age : int = Field ( ge = 0 , le = 100 )
else :
class DemoModel ( BaseModel ): # type: ignore
uid : str = Field ( max_length = 6 , min_length = 6 , pattern = "^u" )
name : str = Field ( min_length = 4 , max_length = 10 )
age : int = Field ( ge = 0 , le = 100 )
@pait ()
async def demo ( demo_model : DemoModel = field . Query . i ( raw_return = True )) -> response . HTTPResponse :
return json ( demo_model . dict ())
@pait ()
async def demo1 ( demo_model : DemoModel = field . Json . i ( raw_return = True )) -> response . HTTPResponse :
return json ( demo_model . dict ())
app = Sanic ( name = "demo" )
app . add_route ( demo , "/api/demo" , methods = [ "GET" ])
app . add_route ( demo1 , "/api/demo1" , methods = [ "POST" ])
if __name__ == "__main__" :
import uvicorn
uvicorn . run ( app )
docs_source_code/introduction/how_to_use_type/tornado_with_model_demo.py from pydantic import BaseModel , Field
from tornado.ioloop import IOLoop
from tornado.web import Application , RequestHandler
from pait import _pydanitc_adapter , field
from pait.app.tornado import pait
if _pydanitc_adapter . is_v1 :
class DemoModel ( BaseModel ):
uid : str = Field ( max_length = 6 , min_length = 6 , regex = "^u" )
name : str = Field ( min_length = 4 , max_length = 10 )
age : int = Field ( ge = 0 , le = 100 )
else :
class DemoModel ( BaseModel ): # type: ignore
uid : str = Field ( max_length = 6 , min_length = 6 , pattern = "^u" )
name : str = Field ( min_length = 4 , max_length = 10 )
age : int = Field ( ge = 0 , le = 100 )
class DemoHandler ( RequestHandler ):
@pait ()
def get ( self , demo_model : DemoModel = field . Query . i ( raw_return = True )) -> None :
self . write ( demo_model . dict ())
class Demo1Handler ( RequestHandler ):
@pait ()
def post ( self , demo_model : DemoModel = field . Json . i ( raw_return = True )) -> None :
self . write ( demo_model . dict ())
app : Application = Application ([( r "/api/demo" , DemoHandler ), ( r "/api/demo1" , Demo1Handler )])
if __name__ == "__main__" :
app . listen ( 8000 )
IOLoop . instance () . start ()
Note
If the raw_return
of the Field
object is not True, Pait
will obtain the value from the requested resource with the Key as demo_model
Next run the code and call the curl
command:
➜ ~ curl "http://127.0.0.1:8000/api/demo?uid=u12345&name=so1n&age=10"
{ "uid" :"u12345" ,"name" :"so1n" ,"age" :10}
➜ ~ curl "http://127.0.0.1:8000/api/demo1" -X POST -d '{"uid": "u12345", "name": "so1n", "age": 10}' --header "Content-Type: application/json"
{ "uid" :"u12345" ,"name" :"so1n" ,"age" :10}
The output shows that both route functions work fine, but in this case all the fields of BaseModel
can only use the same Field
type, so another approach can be taken.
2.The value of Type is the special Pydantic.BaseModel
The Field
object of Pait
is a pydantic.FieldInfo
object that carries the resource identifier, so it can be used in pydantic.BaseModel
.
In this way the DemoModel
object mentioned earlier can be rewritten as follows.
from pait import field
from pydantic import BaseModel
class DemoModel ( BaseModel ):
uid : str = field . Query . i ( max_length = 6 , min_length = 6 , regex = "^u" )
name : str = field . Body . i ( min_length = 4 , max_length = 10 )
age : int = field . Header . i ( ge = 0 , le = 100 )
can see that the Field
object is different for each property of DemoModel
, but in order for Pait
to load DemoModel
correctly the form of the parameters filled in needs to be changed from <name>:<type>=<default>
to <name>:<type>
.
as follows:
@pait ()
def demo ( demo_model : DemoModel ) -> None :
pass
2.1.DefaultField
In addition, can also use the DefaultField
of Pait
, which can automatically replace the Field
of each attribute in pydantic.BaseModel
according to the Field
defined by the route function.
The following is still the same Ordinary DemoModel
:
from pydantic import BaseModel , Field
class DemoModel ( BaseModel ):
uid : str = Field ( max_length = 6 , min_length = 6 , regex = "^u" )
name : str = Field ( min_length = 4 , max_length = 10 )
age : int = Field ( ge = 0 , le = 100 )
Then, The DefaultField
for the demo
route is specified as Query
and the DefaultField
for the demo1
route is specified as Body
via the default_field_class
attribute in the pait
decorator:
from pait import field
from pait.app.any import pait
@pait ( default_field_class = field . Query )
def demo ( demo_model : DemoModel ) -> None :
pass
@pait ( default_field_class = field . Body )
def demo1 ( demo_model : DemoModel ) -> None :
pass
In this way, it is possible to use the same DemoModel
in a route function that uses a different request resource,
the complete code is as follows:
Flask Starlette Sanic Tornado
docs_source_code/introduction/how_to_use_type/flask_with_pait_model_demo.py from uuid import uuid4
from flask import Flask , Response , jsonify
from pydantic import BaseModel , Field
from pait import _pydanitc_adapter , field
from pait.app.flask import pait
if _pydanitc_adapter . is_v1 :
class DemoModel ( BaseModel ):
uid : str = Field ( ... , max_length = 6 , min_length = 6 , regex = "^u" )
name : str = Field ( ... , min_length = 4 , max_length = 10 )
age : int = Field ( ... , ge = 0 , le = 100 )
request_id : str = field . Header . i ( default_factory = lambda : str ( uuid4 ()))
else :
class DemoModel ( BaseModel ): # type: ignore
uid : str = Field ( ... , max_length = 6 , min_length = 6 , pattern = "^u" )
name : str = Field ( ... , min_length = 4 , max_length = 10 )
age : int = Field ( ... , ge = 0 , le = 100 )
request_id : str = field . Header . i ( default_factory = lambda : str ( uuid4 ()))
@pait ( default_field_class = field . Query )
def demo ( demo_model : DemoModel ) -> Response :
return jsonify ( demo_model . dict ())
@pait ( default_field_class = field . Body )
def demo1 ( demo_model : DemoModel ) -> Response :
return jsonify ( demo_model . dict ())
app = Flask ( "demo" )
app . add_url_rule ( "/api/demo" , "demo" , demo , methods = [ "GET" ])
app . add_url_rule ( "/api/demo1" , "demo1" , demo1 , methods = [ "POST" ])
if __name__ == "__main__" :
app . run ( port = 8000 )
docs_source_code/introduction/how_to_use_type/starlette_with_pait_model_demo.py from uuid import uuid4
from pydantic import BaseModel , Field
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from pait import _pydanitc_adapter , field
from pait.app.starlette import pait
if _pydanitc_adapter . is_v1 :
class DemoModel ( BaseModel ):
uid : str = Field ( ... , max_length = 6 , min_length = 6 , regex = "^u" )
name : str = Field ( ... , min_length = 4 , max_length = 10 )
age : int = Field ( ... , ge = 0 , le = 100 )
request_id : str = field . Header . i ( default_factory = lambda : str ( uuid4 ()))
else :
class DemoModel ( BaseModel ): # type: ignore
uid : str = Field ( ... , max_length = 6 , min_length = 6 , pattern = "^u" )
name : str = Field ( ... , min_length = 4 , max_length = 10 )
age : int = Field ( ... , ge = 0 , le = 100 )
request_id : str = field . Header . i ( default_factory = lambda : str ( uuid4 ()))
@pait ( default_field_class = field . Query )
async def demo ( demo_model : DemoModel ) -> JSONResponse :
return JSONResponse ( demo_model . dict ())
@pait ( default_field_class = field . Body )
async def demo1 ( demo_model : DemoModel ) -> JSONResponse :
return JSONResponse ( demo_model . dict ())
app = Starlette ( routes = [ Route ( "/api/demo" , demo , methods = [ "GET" ]), Route ( "/api/demo1" , demo1 , methods = [ "POST" ])])
if __name__ == "__main__" :
import uvicorn
uvicorn . run ( app )
docs_source_code/introduction/how_to_use_type/sanic_with_pait_model_demo.py from uuid import uuid4
from pydantic import BaseModel , Field
from sanic import Sanic , json , response
from pait import _pydanitc_adapter , field
from pait.app.sanic import pait
if _pydanitc_adapter . is_v1 :
class DemoModel ( BaseModel ):
uid : str = Field ( ... , max_length = 6 , min_length = 6 , regex = "^u" )
name : str = Field ( ... , min_length = 4 , max_length = 10 )
age : int = Field ( ... , ge = 0 , le = 100 )
request_id : str = field . Header . i ( default_factory = lambda : str ( uuid4 ()))
else :
class DemoModel ( BaseModel ): # type: ignore
uid : str = Field ( ... , max_length = 6 , min_length = 6 , pattern = "^u" )
name : str = Field ( ... , min_length = 4 , max_length = 10 )
age : int = Field ( ... , ge = 0 , le = 100 )
request_id : str = field . Header . i ( default_factory = lambda : str ( uuid4 ()))
@pait ( default_field_class = field . Query )
async def demo ( demo_model : DemoModel ) -> response . HTTPResponse :
return json ( demo_model . dict ())
@pait ( default_field_class = field . Body )
async def demo1 ( demo_model : DemoModel ) -> response . HTTPResponse :
return json ( demo_model . dict ())
app = Sanic ( name = "demo" )
app . add_route ( demo , "/api/demo" , methods = [ "GET" ])
app . add_route ( demo1 , "/api/demo1" , methods = [ "POST" ])
if __name__ == "__main__" :
import uvicorn
uvicorn . run ( app )
docs_source_code/introduction/how_to_use_type/tornado_with_pait_model_demo.py from uuid import uuid4
from pydantic import BaseModel , Field
from tornado.ioloop import IOLoop
from tornado.web import Application , RequestHandler
from pait import _pydanitc_adapter , field
from pait.app.tornado import pait
if _pydanitc_adapter . is_v1 :
class DemoModel ( BaseModel ):
uid : str = Field ( ... , max_length = 6 , min_length = 6 , regex = "^u" )
name : str = Field ( ... , min_length = 4 , max_length = 10 )
age : int = Field ( ... , ge = 0 , le = 100 )
request_id : str = field . Header . i ( default_factory = lambda : str ( uuid4 ()))
else :
class DemoModel ( BaseModel ): # type: ignore
uid : str = Field ( ... , max_length = 6 , min_length = 6 , pattern = "^u" )
name : str = Field ( ... , min_length = 4 , max_length = 10 )
age : int = Field ( ... , ge = 0 , le = 100 )
request_id : str = field . Header . i ( default_factory = lambda : str ( uuid4 ()))
class DemoHandler ( RequestHandler ):
@pait ( default_field_class = field . Query )
def get ( self , demo_model : DemoModel ) -> None :
self . write ( demo_model . dict ())
class Demo1Handler ( RequestHandler ):
@pait ( default_field_class = field . Body )
def post ( self , demo_model : DemoModel ) -> None :
self . write ( demo_model . dict ())
app : Application = Application ([( r "/api/demo" , DemoHandler ), ( r "/api/demo1" , Demo1Handler )])
if __name__ == "__main__" :
app . listen ( 8000 )
IOLoop . instance () . start ()
The DemoModel
in the code adds a property named request_id
, which uses a Field
object that is a Header
object.
Since the Default Field
function will only replace pydantic.FieldInfo
, this means that the request_id
property will not be affected by Default Field
during runtime, it will still get the data from the Header.
Now running the code and calling the curl
command to see the following output:
➜ ~ curl "http://127.0.0.1:8000/api/demo?uid=u12345&name=so1n&age=10"
{ "age" :10,"name" :"so1n" ,"request_id" :"6a5827f7-5767-46ef-91c6-f2ae99770865" ,"uid" :"u12345" }
➜ ~ curl "http://127.0.0.1:8000/api/demo1" -X POST -d '{"uid": "u12345", "name": "so1n", "age": 10}' --header "Content-Type: application/json"
{ "age" :10,"name" :"so1n" ,"request_id" :"3279944c-6de7-4270-8536-33619641f25e" ,"uid" :"u12345" }
Note
Note that under normal circumstances,
Pait
processes/checks the values of each variable in the order of the variables and throws an error if it finds an illegitimate value, and doesn't process the rest of the values.
However, when the value of Type
is pydantic.BaseModel
, Pait
will delegate the parameter to pydantic.BaseModel
, and pydantic.BaseModel
will check all the values, and then throw the error.
3.Other
3.1.Request Obj
When using Pait
, the Request
object is used significantly less often, so Pait
automatically omits the Request
object, for example, the Starlette
framework route function code is as follows:
from starlette.requests import Request
async def demo ( request : Request ) -> None :
pass
After using Pait
in the route function, the code is as follows:
from pait.app.starlette import pait
@pait ()
async def demo ():
pass
Note
Note:The Sanic
framework requires that route functions must have at least one parameter.
If need to use the Request
object in the route function,
need to define the route function parameters in the form of <var name>: <RequestType>
so that pait
can correctly inject the Request
object into the corresponding variable.
For example:
Flask Starlette Sanic Tornado
docs_source_code/introduction/how_to_use_type/flask_with_request_demo.py from flask import Flask , Request , Response , jsonify
from pait.app.flask import pait
@pait ()
def demo ( req : Request ) -> Response :
return jsonify ({ "url" : req . path , "method" : req . method })
app = Flask ( "demo" )
app . add_url_rule ( "/api/demo" , "demo" , demo , methods = [ "GET" ])
if __name__ == "__main__" :
app . run ( port = 8000 )
docs_source_code/introduction/how_to_use_type/starlette_with_request_demo.py from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
from pait.app.starlette import pait
@pait ()
async def demo ( req : Request ) -> JSONResponse :
return JSONResponse ({ "url" : str ( req . url . path ), "method" : req . method })
app = Starlette (
routes = [
Route ( "/api/demo" , demo , methods = [ "GET" ]),
]
)
if __name__ == "__main__" :
import uvicorn
uvicorn . run ( app )
docs_source_code/introduction/how_to_use_type/sanic_with_request_demo.py from sanic import Request , Sanic , json , response
from pait.app.sanic import pait
@pait ()
async def demo ( req : Request ) -> response . HTTPResponse :
return json ({ "url" : req . path , "method" : req . method })
app = Sanic ( name = "demo" )
app . add_route ( demo , "/api/demo" , methods = [ "GET" ])
if __name__ == "__main__" :
import uvicorn
uvicorn . run ( app )
docs_source_code/introduction/how_to_use_type/tornado_with_request_demo.py from tornado.httputil import HTTPServerRequest
from tornado.ioloop import IOLoop
from tornado.web import Application , RequestHandler
from pait.app.tornado import pait
class DemoHandler ( RequestHandler ):
@pait ()
def get ( self , req : HTTPServerRequest ) -> None :
self . write ({ "url" : req . uri , "method" : req . method })
app : Application = Application (
[
( r "/api/demo" , DemoHandler ),
]
)
if __name__ == "__main__" :
app . listen ( 8000 )
IOLoop . instance () . start ()
Run the code and use the curl
command to see the following output:
curl "http://127.0.0.1:8000/api/demo"
{ "url" : "http://127.0.0.1:8000/api/demo" , "method" : "GET" }
3.2.How to customize a Type that meets Pydantic requirements and has verification function
As mentioned earlier, the Type
used in a Pait
decorated route function does the same thing as the Type
of Pydantic,
which means that the Type
can be used to extend the checking rules to make up for the lack of a Field
object,
For example, in a business where users may be located in different countries,
it is common to use timestamps to pass the time in order to prevent data errors caused by different time zones, as follows:
Flask Starlette Sanic Tornado
docs_source_code/introduction/how_to_use_type/flask_with_datetime_demo.py import datetime
from flask import Flask , Response , jsonify
from pait import field
from pait.app.flask import pait
@pait ()
def demo ( timestamp : datetime . datetime = field . Query . i ()) -> Response :
return jsonify ({ "time" : timestamp . isoformat ()})
app = Flask ( "demo" )
app . add_url_rule ( "/api/demo" , "demo" , demo , methods = [ "GET" ])
if __name__ == "__main__" :
app . run ( port = 8000 )
docs_source_code/introduction/how_to_use_type/starlette_with_datetime_demo.py import datetime
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from pait import field
from pait.app.starlette import pait
@pait ()
async def demo ( timestamp : datetime . datetime = field . Query . i ()) -> JSONResponse :
return JSONResponse ({ "time" : timestamp . isoformat ()})
app = Starlette (
routes = [
Route ( "/api/demo" , demo , methods = [ "GET" ]),
]
)
if __name__ == "__main__" :
import uvicorn
uvicorn . run ( app )
docs_source_code/introduction/how_to_use_type/sanic_with_datetime_demo.py import datetime
from sanic import Sanic , json , response
from pait import field
from pait.app.sanic import pait
@pait ()
async def demo ( timestamp : datetime . datetime = field . Query . i ()) -> response . HTTPResponse :
return json ({ "time" : timestamp . isoformat ()})
app = Sanic ( name = "demo" )
app . add_route ( demo , "/api/demo" , methods = [ "GET" ])
if __name__ == "__main__" :
import uvicorn
uvicorn . run ( app )
docs_source_code/introduction/how_to_use_type/tornado_with_datetime_demo.py import datetime
from tornado.ioloop import IOLoop
from tornado.web import Application , RequestHandler
from pait import field
from pait.app.tornado import pait
class DemoHandler ( RequestHandler ):
@pait ()
def get ( self , timestamp : datetime . datetime = field . Query . i ()) -> None :
self . write ({ "time" : timestamp . isoformat ()})
app : Application = Application (
[
( r "/api/demo" , DemoHandler ),
]
)
if __name__ == "__main__" :
app . listen ( 8000 )
IOLoop . instance () . start ()
However, after running the code and calling the following curl
command,
can find that Pydantic
automatically converts the timestamp to datetime type and the time zone is UTC:
➜ ~ curl "http://127.0.0.1:8000/api/demo?timestamp=1600000000"
{ "time" :"2020-09-13T12:26:40+00:00" }
This processing is fine, but assuming that the server for this business is located in some non-UTC and both the database and the program's are dependent on the time zone of the machine, the
It would be necessary to perform another time zone conversion each time the data is fetched.
This can be solved by writing a Type
class that conforms to the Pydantic
checksum rule, code show as below:
Pydantic V1 Pydantic V2
import datetime
from typing import Callable , Generator , Union
class UnixDatetime ( datetime . datetime ):
@classmethod
def __get_validators__ ( cls ) -> Generator [ Callable , None , None ]:
yield cls . validate
@classmethod
def validate ( cls , v : Union [ int , str ]) -> datetime . datetime :
if isinstance ( v , str ):
v = int ( v )
return datetime . datetime . fromtimestamp ( v )
from pydantic import BeforeValidator
from typing import Union
from typing_extensions import Annotated
from datetime import datetime
def validate ( v : Union [ int , str ]) -> datetime :
if isinstance ( v , str ):
v = int ( v )
return datetime . fromtimestamp ( v )
UnixDatetime = Annotated [ datetime , BeforeValidator ( validate )]
through the sample code can be seen due to Pydantic
version of the different, Type
implementation is not the same, but their logic is the same, write the completion of the Type
can be applied to the code:
Flask Starlette Sanic Tornado
docs_source_code/introduction/how_to_use_type/flask_with_unix_datetime_demo.py import datetime
from typing import Callable , Generator , Union
from flask import Flask , Response , jsonify
from pait import field
from pait._pydanitc_adapter import is_v1
from pait.app.flask import pait
if is_v1 :
class UnixDatetime ( datetime . datetime ):
@classmethod
def __get_validators__ ( cls ) -> Generator [ Callable , None , None ]:
yield cls . validate
@classmethod
def validate ( cls , v : Union [ int , str ]) -> datetime . datetime :
if isinstance ( v , str ):
v = int ( v )
return datetime . datetime . fromtimestamp ( v )
else :
from pydantic import BeforeValidator
from typing_extensions import Annotated
def validate ( v : Union [ int , str ]) -> datetime . datetime :
if isinstance ( v , str ):
v = int ( v )
return datetime . datetime . fromtimestamp ( v )
UnixDatetime = Annotated [ datetime . datetime , BeforeValidator ( validate )] # type: ignore
@pait ()
def demo ( timestamp : UnixDatetime = field . Query . i ()) -> Response :
return jsonify ({ "time" : timestamp . isoformat ()})
app = Flask ( "demo" )
app . add_url_rule ( "/api/demo" , "demo" , demo , methods = [ "GET" ])
if __name__ == "__main__" :
app . run ( port = 8000 )
docs_source_code/introduction/how_to_use_type/starlette_with_unix_datetime_demo.py import datetime
from typing import Callable , Generator , Union
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from pait import field
from pait._pydanitc_adapter import is_v1
from pait.app.starlette import pait
if is_v1 :
class UnixDatetime ( datetime . datetime ):
@classmethod
def __get_validators__ ( cls ) -> Generator [ Callable , None , None ]:
yield cls . validate
@classmethod
def validate ( cls , v : Union [ int , str ]) -> datetime . datetime :
if isinstance ( v , str ):
v = int ( v )
return datetime . datetime . fromtimestamp ( v )
else :
from pydantic import BeforeValidator
from typing_extensions import Annotated
def validate ( v : Union [ int , str ]) -> datetime . datetime :
if isinstance ( v , str ):
v = int ( v )
return datetime . datetime . fromtimestamp ( v )
UnixDatetime = Annotated [ datetime . datetime , BeforeValidator ( validate )] # type: ignore
@pait ()
async def demo ( timestamp : UnixDatetime = field . Query . i ()) -> JSONResponse :
return JSONResponse ({ "time" : timestamp . isoformat ()})
app = Starlette (
routes = [
Route ( "/api/demo" , demo , methods = [ "GET" ]),
]
)
if __name__ == "__main__" :
import uvicorn
uvicorn . run ( app )
docs_source_code/introduction/how_to_use_type/sanic_with_unix_datetime_demo.py import datetime
from typing import Callable , Generator , Union
from sanic import Sanic , json , response
from pait import field
from pait._pydanitc_adapter import is_v1
from pait.app.sanic import pait
if is_v1 :
class UnixDatetime ( datetime . datetime ):
@classmethod
def __get_validators__ ( cls ) -> Generator [ Callable , None , None ]:
yield cls . validate
@classmethod
def validate ( cls , v : Union [ int , str ]) -> datetime . datetime :
if isinstance ( v , str ):
v = int ( v )
return datetime . datetime . fromtimestamp ( v )
else :
from pydantic import BeforeValidator
from typing_extensions import Annotated
def validate ( v : Union [ int , str ]) -> datetime . datetime :
if isinstance ( v , str ):
v = int ( v )
return datetime . datetime . fromtimestamp ( v )
UnixDatetime = Annotated [ datetime . datetime , BeforeValidator ( validate )] # type: ignore
@pait ()
async def demo ( timestamp : UnixDatetime = field . Query . i ()) -> response . HTTPResponse :
return json ({ "time" : timestamp . isoformat ()})
app = Sanic ( name = "demo" )
app . add_route ( demo , "/api/demo" , methods = [ "GET" ])
if __name__ == "__main__" :
import uvicorn
uvicorn . run ( app )
docs_source_code/introduction/how_to_use_type/tornado_with_unix_datetime_demo.py import datetime
from typing import Callable , Generator , Union
from tornado.ioloop import IOLoop
from tornado.web import Application , RequestHandler
from pait import field
from pait._pydanitc_adapter import is_v1
from pait.app.tornado import pait
if is_v1 :
class UnixDatetime ( datetime . datetime ):
@classmethod
def __get_validators__ ( cls ) -> Generator [ Callable , None , None ]:
yield cls . validate
@classmethod
def validate ( cls , v : Union [ int , str ]) -> datetime . datetime :
if isinstance ( v , str ):
v = int ( v )
return datetime . datetime . fromtimestamp ( v )
else :
from pydantic import BeforeValidator
from typing_extensions import Annotated
def validate ( v : Union [ int , str ]) -> datetime . datetime :
if isinstance ( v , str ):
v = int ( v )
return datetime . datetime . fromtimestamp ( v )
UnixDatetime = Annotated [ datetime . datetime , BeforeValidator ( validate )] # type: ignore
class DemoHandler ( RequestHandler ):
@pait ()
def get ( self , timestamp : UnixDatetime = field . Query . i ()) -> None :
self . write ({ "time" : timestamp . isoformat ()})
app : Application = Application (
[
( r "/api/demo" , DemoHandler ),
]
)
if __name__ == "__main__" :
app . listen ( 8000 )
IOLoop . instance () . start ()
After re-running the modified code and calling the curl
command,
can find that the returned time value no longer has the time zone:
➜ ~ curl "http://127.0.0.1:8000/api/demo?timestamp=1600000000"
{ "time" :"2020-09-13T20:26:40" }