fastapi-extra 0.1.0__cp312-cp312-win_amd64.whl → 0.1.2__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_extra/__init__.py +8 -6
- fastapi_extra/cursor.cp312-win_amd64.pyd +0 -0
- fastapi_extra/dependency.py +38 -0
- fastapi_extra/form.py +5 -0
- fastapi_extra/native/routing.pyx +45 -76
- fastapi_extra/routing.cp312-win_amd64.pyd +0 -0
- fastapi_extra/settings.py +13 -2
- fastapi_extra/types.py +12 -1
- {fastapi_extra-0.1.0.dist-info → fastapi_extra-0.1.2.dist-info}/METADATA +2 -2
- fastapi_extra-0.1.2.dist-info/RECORD +16 -0
- {fastapi_extra-0.1.0.dist-info → fastapi_extra-0.1.2.dist-info}/WHEEL +1 -1
- fastapi_extra/databases.py +0 -96
- fastapi_extra/model.py +0 -73
- fastapi_extra-0.1.0.dist-info/RECORD +0 -17
- {fastapi_extra-0.1.0.dist-info → fastapi_extra-0.1.2.dist-info}/LICENSE +0 -0
- {fastapi_extra-0.1.0.dist-info → fastapi_extra-0.1.2.dist-info}/top_level.txt +0 -0
fastapi_extra/__init__.py
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.2"
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
from fastapi import routing
|
|
4
|
+
from fastapi import FastAPI
|
|
5
|
+
|
|
7
6
|
|
|
7
|
+
def install(app: FastAPI) -> None:
|
|
8
|
+
try:
|
|
8
9
|
from fastapi_extra import routing as native_routing # type: ignore
|
|
10
|
+
from fastapi_extra import dependency
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
native_routing.install(app)
|
|
13
|
+
app.dependency_overrides |= dependency.default_dependency_override
|
|
12
14
|
except ImportError: # pragma: nocover
|
|
13
15
|
pass
|
|
Binary file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
__author__ = "ziyan.yin"
|
|
2
|
+
__date__ = "2025-01-05"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from abc import ABCMeta
|
|
6
|
+
from typing import Annotated, Any
|
|
7
|
+
|
|
8
|
+
from fastapi.params import Depends
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
default_dependency_override = {}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DependencyMetaClass(ABCMeta):
|
|
15
|
+
__root__: Any = None
|
|
16
|
+
|
|
17
|
+
def __new__(
|
|
18
|
+
mcs,
|
|
19
|
+
name: str,
|
|
20
|
+
bases: tuple[type, ...],
|
|
21
|
+
attrs: dict,
|
|
22
|
+
root: bool = False,
|
|
23
|
+
override: bool = False
|
|
24
|
+
):
|
|
25
|
+
new_cls = super().__new__(mcs, name, bases, attrs)
|
|
26
|
+
if not root:
|
|
27
|
+
if new_cls.__root__ is None:
|
|
28
|
+
new_cls.__root__ = new_cls
|
|
29
|
+
elif override:
|
|
30
|
+
default_dependency_override[new_cls.__root__] = new_cls
|
|
31
|
+
return Annotated[new_cls, Depends(new_cls)]
|
|
32
|
+
return new_cls
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Service(metaclass=DependencyMetaClass, root=True):
|
|
37
|
+
__slot__ = ()
|
|
38
|
+
|
fastapi_extra/form.py
CHANGED
|
@@ -28,3 +28,8 @@ class ColumnExpression(BaseModel, Generic[S]):
|
|
|
28
28
|
class WhereClause(BaseModel):
|
|
29
29
|
option: Literal["and", "or"] = Field(default="and", title="关系")
|
|
30
30
|
column_clauses: list[ColumnExpression | "WhereClause"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class LoginForm(BaseModel):
|
|
34
|
+
username: str = Field(title="用户名")
|
|
35
|
+
password: str = Field(title="密码")
|
fastapi_extra/native/routing.pyx
CHANGED
|
@@ -5,21 +5,11 @@ cimport cython
|
|
|
5
5
|
|
|
6
6
|
from typing import MutableMapping
|
|
7
7
|
|
|
8
|
-
from basex.common import strings
|
|
9
|
-
from fastapi import APIRouter
|
|
10
8
|
from starlette import _utils as starlette_utils
|
|
11
9
|
from starlette.datastructures import URL
|
|
12
10
|
from starlette.responses import RedirectResponse
|
|
13
11
|
|
|
14
12
|
|
|
15
|
-
def get_route_path(scope: MutableMapping) -> str:
|
|
16
|
-
root_path = scope.get("root_path", "")
|
|
17
|
-
route_path = scope["path"].removeprefix(root_path)
|
|
18
|
-
return route_path
|
|
19
|
-
|
|
20
|
-
starlette_utils.get_route_path = get_route_path
|
|
21
|
-
|
|
22
|
-
|
|
23
13
|
@cython.no_gc
|
|
24
14
|
cdef class RouteNode:
|
|
25
15
|
cdef readonly:
|
|
@@ -78,86 +68,65 @@ cdef list find_routes(unicode path, RouteNode root):
|
|
|
78
68
|
routes += current_node.routes
|
|
79
69
|
continue
|
|
80
70
|
break
|
|
81
|
-
|
|
82
|
-
routes.reverse()
|
|
83
71
|
return routes
|
|
84
72
|
|
|
85
73
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
class BaseRouter(_super_router):
|
|
90
|
-
|
|
91
|
-
def __init__(self, *args, **kwargs):
|
|
92
|
-
super().__init__(*args, **kwargs)
|
|
93
|
-
self.root_node = RouteNode.__new__(RouteNode, '')
|
|
94
|
-
for route in self.routes:
|
|
95
|
-
add_route(route.path, self.root_node, route)
|
|
74
|
+
root_node = RouteNode.__new__(RouteNode, "")
|
|
96
75
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
add_route(self.routes[-1].path, self.root_node, self.routes[-1])
|
|
76
|
+
async def handle(router, scope, receive, send):
|
|
77
|
+
assert scope["type"] in ("http", "websocket", "lifespan")
|
|
100
78
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
add_route(self.routes[-1].path, self.root_node, self.routes[-1])
|
|
79
|
+
if "router" not in scope:
|
|
80
|
+
scope["router"] = router
|
|
104
81
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
82
|
+
if scope["type"] == "lifespan":
|
|
83
|
+
await router.lifespan(scope, receive, send)
|
|
84
|
+
return
|
|
108
85
|
|
|
109
|
-
|
|
110
|
-
super().add_api_websocket_route(path, endpoint, **kwargs)
|
|
111
|
-
add_route(self.routes[-1].path, self.root_node, self.routes[-1])
|
|
86
|
+
partial = None
|
|
112
87
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
88
|
+
scope["path"] = route_path = starlette_utils.get_route_path(scope)
|
|
89
|
+
scope["root_path"] = ""
|
|
90
|
+
matched_routes = find_routes(route_path, root_node)
|
|
91
|
+
n = len(matched_routes)
|
|
116
92
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if
|
|
121
|
-
scope
|
|
122
|
-
|
|
123
|
-
if scope["type"] == "lifespan":
|
|
124
|
-
await self.lifespan(scope, receive, send)
|
|
93
|
+
for i in range(n):
|
|
94
|
+
route = matched_routes[n - i - 1]
|
|
95
|
+
match, child_scope = route.matches(scope)
|
|
96
|
+
if match.value == 2:
|
|
97
|
+
scope.update(child_scope)
|
|
98
|
+
await route.handle(scope, receive, send)
|
|
125
99
|
return
|
|
100
|
+
elif match.value == 1 and partial is None:
|
|
101
|
+
partial = route
|
|
102
|
+
partial_scope = child_scope
|
|
126
103
|
|
|
127
|
-
|
|
104
|
+
if partial is not None:
|
|
105
|
+
scope.update(partial_scope)
|
|
106
|
+
await partial.handle(scope, receive, send)
|
|
107
|
+
return
|
|
128
108
|
|
|
129
|
-
route_path = get_route_path(scope)
|
|
130
|
-
matched_routes = find_routes(route_path, self.root_node)
|
|
131
109
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
110
|
+
if scope["type"] == "http" and router.redirect_slashes and route_path != "/":
|
|
111
|
+
redirect_scope = dict(scope)
|
|
112
|
+
if route_path.endswith("/"):
|
|
113
|
+
redirect_scope["path"] = redirect_scope["path"].rstrip("/")
|
|
114
|
+
else:
|
|
115
|
+
redirect_scope["path"] = redirect_scope["path"] + "/"
|
|
116
|
+
|
|
117
|
+
for i in range(n):
|
|
118
|
+
route = matched_routes[n - i - 1]
|
|
119
|
+
match, child_scope = route.matches(redirect_scope)
|
|
120
|
+
if match.value != 0:
|
|
121
|
+
redirect_url = URL(scope=redirect_scope)
|
|
122
|
+
response = RedirectResponse(url=str(redirect_url))
|
|
123
|
+
await response(scope, receive, send)
|
|
137
124
|
return
|
|
138
|
-
elif match.value == 1 and partial is None:
|
|
139
|
-
partial = route
|
|
140
|
-
partial_scope = child_scope
|
|
141
|
-
|
|
142
|
-
if partial is not None:
|
|
143
|
-
scope.update(partial_scope)
|
|
144
|
-
await partial.handle(scope, receive, send)
|
|
145
|
-
return
|
|
146
|
-
|
|
147
125
|
|
|
148
|
-
|
|
149
|
-
redirect_scope = dict(scope)
|
|
150
|
-
if route_path.endswith("/"):
|
|
151
|
-
redirect_scope["path"] = redirect_scope["path"].rstrip("/")
|
|
152
|
-
else:
|
|
153
|
-
redirect_scope["path"] = redirect_scope["path"] + "/"
|
|
126
|
+
await router.default(scope, receive, send)
|
|
154
127
|
|
|
155
|
-
for route in matched_routes:
|
|
156
|
-
match, child_scope = route.matches(redirect_scope)
|
|
157
|
-
if match.value != 0:
|
|
158
|
-
redirect_url = URL(scope=redirect_scope)
|
|
159
|
-
response = RedirectResponse(url=str(redirect_url))
|
|
160
|
-
await response(scope, receive, send)
|
|
161
|
-
return
|
|
162
128
|
|
|
163
|
-
|
|
129
|
+
def install(app):
|
|
130
|
+
for route in app.routes:
|
|
131
|
+
add_route(route.path, root_node, route)
|
|
132
|
+
app.router.app = handle
|
|
Binary file
|
fastapi_extra/settings.py
CHANGED
|
@@ -4,6 +4,7 @@ __date__ = "2024-12-26"
|
|
|
4
4
|
|
|
5
5
|
from typing import Literal, Tuple, Type
|
|
6
6
|
|
|
7
|
+
from pydantic import model_validator
|
|
7
8
|
from pydantic_settings import (BaseSettings, PydanticBaseSettingsSource,
|
|
8
9
|
SettingsConfigDict, TomlConfigSettingsSource)
|
|
9
10
|
|
|
@@ -11,12 +12,15 @@ from pydantic_settings import (BaseSettings, PydanticBaseSettingsSource,
|
|
|
11
12
|
class Settings(BaseSettings):
|
|
12
13
|
model_config = SettingsConfigDict(
|
|
13
14
|
toml_file=["config.default.toml", "config.custom.toml"],
|
|
14
|
-
validate_default=False
|
|
15
|
+
validate_default=False,
|
|
16
|
+
extra="ignore"
|
|
15
17
|
)
|
|
16
18
|
|
|
17
|
-
title: str = ""
|
|
19
|
+
title: str = "FastAPI"
|
|
18
20
|
version: str = "0.1.0"
|
|
19
21
|
debug: bool = False
|
|
22
|
+
root_path: str = ""
|
|
23
|
+
include_in_schema: bool = True
|
|
20
24
|
mode: Literal["dev", "test", "prod"] = "dev"
|
|
21
25
|
|
|
22
26
|
@classmethod
|
|
@@ -34,3 +38,10 @@ class Settings(BaseSettings):
|
|
|
34
38
|
init_settings,
|
|
35
39
|
file_secret_settings
|
|
36
40
|
)
|
|
41
|
+
|
|
42
|
+
@model_validator(mode="after")
|
|
43
|
+
def validate_mode(self) -> "Settings":
|
|
44
|
+
if self.mode == "prod":
|
|
45
|
+
self.include_in_schema = False
|
|
46
|
+
self.debug = False
|
|
47
|
+
return self
|
fastapi_extra/types.py
CHANGED
|
@@ -4,7 +4,10 @@ __date__ = "2024-12-25"
|
|
|
4
4
|
|
|
5
5
|
import datetime
|
|
6
6
|
import decimal
|
|
7
|
-
from typing import Any, TypeVar, Union
|
|
7
|
+
from typing import Annotated, Any, TypeVar, Union
|
|
8
|
+
|
|
9
|
+
from pydantic import PlainSerializer
|
|
10
|
+
from sqlmodel import SQLModel
|
|
8
11
|
|
|
9
12
|
Comparable = Union[int, float, decimal.Decimal, datetime.datetime, datetime.date, datetime.time]
|
|
10
13
|
Serializable = Union[Comparable, bool, str, None]
|
|
@@ -14,3 +17,11 @@ T = TypeVar("T", bound=Any)
|
|
|
14
17
|
E = TypeVar("E", bound=Exception)
|
|
15
18
|
C = TypeVar("C", bound=Comparable)
|
|
16
19
|
S = TypeVar("S", bound=Serializable)
|
|
20
|
+
Model = TypeVar("Model", bound=SQLModel)
|
|
21
|
+
|
|
22
|
+
Cursor = Annotated[
|
|
23
|
+
int, PlainSerializer(lambda x: str(x), return_type=str)
|
|
24
|
+
]
|
|
25
|
+
LocalDateTime = Annotated[
|
|
26
|
+
datetime.datetime, PlainSerializer(lambda x: x.strftime("%Y-%m-%d %H:%M:%S"), return_type=str)
|
|
27
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
fastapi_extra/__init__.py,sha256=Q--tEaIQ892MOwYjKzWvrpUD2H3srBTbCBDRDBAFRqE,410
|
|
2
|
+
fastapi_extra/cursor.cp312-win_amd64.pyd,sha256=g21HsiTvEovTioMgGGC9VKnMRhtTH6qOOOxCh7w9ffg,57344
|
|
3
|
+
fastapi_extra/dependency.py,sha256=fs-WUmYImz8UUGC2LkOxn_3w1H_iVA4dVha8oiz6hKA,878
|
|
4
|
+
fastapi_extra/form.py,sha256=h6xaWF8sjnD4uinBRWnFbgWgaxerYjzUAROXQiXyFBk,1079
|
|
5
|
+
fastapi_extra/response.py,sha256=DHvhOSgwot5eBNKuI_jPYxZ5rshZ55Xkg-FNBJlHD1E,9609
|
|
6
|
+
fastapi_extra/routing.cp312-win_amd64.pyd,sha256=14ZPwBtxTMT2rHqp9sGq3vxDsfCO5O9geq8QlTdL1lI,94208
|
|
7
|
+
fastapi_extra/settings.py,sha256=cCcwaper5GiNNoT4gNKqf-iloSOTNnMsiUR0knJx4Mw,1461
|
|
8
|
+
fastapi_extra/types.py,sha256=EUjT9jFryzlazHvWs4m-IfUezmSEvyxwaOGe_vTTBnY,763
|
|
9
|
+
fastapi_extra/utils.py,sha256=tsPX3kpF_P5D9Bd3gnlG6rkVsLkv5gbxjml-s6ZL_6I,346
|
|
10
|
+
fastapi_extra/native/cursor.pyx,sha256=bESprFDgk9gGjyPQ4YCSg51dov2WB6s60XrOs3r5-r0,1146
|
|
11
|
+
fastapi_extra/native/routing.pyx,sha256=sByKvUrILFKkEky2nhlzA4vLJsmvk1B2cZs0s2yH1g0,3762
|
|
12
|
+
fastapi_extra-0.1.2.dist-info/LICENSE,sha256=0vTjHDa3VDsxTT-R-sH6SpYcA2F1hKtbX9ZFZQm-EcU,1516
|
|
13
|
+
fastapi_extra-0.1.2.dist-info/METADATA,sha256=aSjC76xJbexcUAHmFUPFabhn11xEaBcYkW5pK-mNwVI,1006
|
|
14
|
+
fastapi_extra-0.1.2.dist-info/WHEEL,sha256=cRmSBGD-cl98KkuHMNqv9Ac9L9_VqTvcBYwpIvxN0cg,101
|
|
15
|
+
fastapi_extra-0.1.2.dist-info/top_level.txt,sha256=B7D80bEftE2E-eSd1be2r9BWkLLMZN21dRTWpb4y4Ig,14
|
|
16
|
+
fastapi_extra-0.1.2.dist-info/RECORD,,
|
fastapi_extra/databases.py
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
__author__ = "ziyan.yin"
|
|
2
|
-
__date__ = "2024-12-26"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
from typing import Annotated, Any, Literal
|
|
6
|
-
|
|
7
|
-
from fastapi.params import Depends
|
|
8
|
-
from pydantic import AnyUrl, BaseModel
|
|
9
|
-
from sqlalchemy import Engine, NullPool
|
|
10
|
-
from sqlalchemy.ext.asyncio import AsyncEngine
|
|
11
|
-
from sqlalchemy.ext.asyncio import AsyncSession as _AsyncSession
|
|
12
|
-
from sqlalchemy.orm import Session as _Session
|
|
13
|
-
from sqlalchemy.util import _concurrency_py3k
|
|
14
|
-
from sqlmodel import create_engine
|
|
15
|
-
|
|
16
|
-
from fastapi_extra.settings import Settings
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class DatabaseConfig(BaseModel):
|
|
20
|
-
url: AnyUrl
|
|
21
|
-
echo: bool = False
|
|
22
|
-
echo_pool: bool = False
|
|
23
|
-
isolation_level: Literal[
|
|
24
|
-
"SERIALIZABLE",
|
|
25
|
-
"REPEATABLE READ",
|
|
26
|
-
"READ COMMITTED",
|
|
27
|
-
"READ UNCOMMITTED",
|
|
28
|
-
"AUTOCOMMIT",
|
|
29
|
-
] | None = None
|
|
30
|
-
max_overflow: int = 10
|
|
31
|
-
pool_pre_ping: bool = False
|
|
32
|
-
pool_size: int = 5
|
|
33
|
-
pool_recycle: int = -1
|
|
34
|
-
pool_timeout: int = 30
|
|
35
|
-
pool_use_lifo: bool = False
|
|
36
|
-
query_cache_size: int = 500
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class DatabaseSettings(Settings):
|
|
40
|
-
datasources: dict[str, DatabaseConfig]
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
_settings = DatabaseSettings() # type: ignore
|
|
44
|
-
_engines: dict[str, Engine] = {}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def load_engine(name: str = "default", **kw: Any) -> Engine:
|
|
48
|
-
if name in _engines:
|
|
49
|
-
return _engines[name]
|
|
50
|
-
if name in _settings.datasources:
|
|
51
|
-
config = _settings.datasources[name]
|
|
52
|
-
_engines[name] = create_engine(
|
|
53
|
-
url=str(config.url),
|
|
54
|
-
**config.model_dump(exclude_defaults=True, exclude={"url"}),
|
|
55
|
-
**kw
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
return _engines[name]
|
|
59
|
-
|
|
60
|
-
raise KeyError(f"cannot find datasources.{name}")
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
async def shutdown() -> None:
|
|
64
|
-
for engine in _engines.values():
|
|
65
|
-
await _concurrency_py3k.greenlet_spawn(engine.dispose)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class SessionFactory(Depends):
|
|
69
|
-
__slots__ = ("engine", )
|
|
70
|
-
datasource: str = "default"
|
|
71
|
-
|
|
72
|
-
def __init__(self):
|
|
73
|
-
super().__init__()
|
|
74
|
-
if _settings.mode == "test":
|
|
75
|
-
self.engine = load_engine(self.datasource, poolclass=NullPool)
|
|
76
|
-
else:
|
|
77
|
-
self.engine = load_engine(self.datasource)
|
|
78
|
-
self.dependency = self
|
|
79
|
-
|
|
80
|
-
def __call__(self):
|
|
81
|
-
with _Session(self.engine) as session:
|
|
82
|
-
yield session
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class AsyncSessionFactory(SessionFactory):
|
|
86
|
-
|
|
87
|
-
def __init__(self):
|
|
88
|
-
super().__init__()
|
|
89
|
-
self.engine = AsyncEngine(self.engine)
|
|
90
|
-
|
|
91
|
-
async def __call__(self):
|
|
92
|
-
async with _AsyncSession(self.engine) as session:
|
|
93
|
-
yield session
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
AsyncSession = Annotated[_AsyncSession, AsyncSessionFactory()]
|
fastapi_extra/model.py
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
__author__ = "ziyan.yin"
|
|
2
|
-
__date__ = "2024-12-25"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import datetime
|
|
6
|
-
from typing import Annotated
|
|
7
|
-
|
|
8
|
-
from pydantic import PlainSerializer
|
|
9
|
-
from sqlalchemy import BigInteger, DateTime, Integer, SmallInteger, func
|
|
10
|
-
from sqlalchemy.ext.declarative import declared_attr
|
|
11
|
-
from sqlmodel import Field, SQLModel
|
|
12
|
-
|
|
13
|
-
from fastapi_extra.cursor import Cursor as _Cursor # type: ignore
|
|
14
|
-
from fastapi_extra.utils import get_machine_seed
|
|
15
|
-
|
|
16
|
-
Cursor = Annotated[
|
|
17
|
-
int, PlainSerializer(lambda x: str(x), return_type=str)
|
|
18
|
-
]
|
|
19
|
-
LocalDateTime = Annotated[
|
|
20
|
-
datetime.datetime, PlainSerializer(lambda x: x.strftime("%Y-%m-%d %H:%M:%S"), return_type=str)
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class SQLBase(SQLModel):
|
|
25
|
-
id: int | None = Field(
|
|
26
|
-
default_factory=lambda: None,
|
|
27
|
-
title="ID",
|
|
28
|
-
primary_key=True
|
|
29
|
-
)
|
|
30
|
-
create_at: LocalDateTime = Field(
|
|
31
|
-
default_factory=datetime.datetime.now,
|
|
32
|
-
title="CREATE_AT",
|
|
33
|
-
sa_type=DateTime,
|
|
34
|
-
sa_column_kwargs={"default": func.now(), "nullable": False, "comment": "CREATE_AT"},
|
|
35
|
-
schema_extra={"json_schema_extra": {"readOnly": True}},
|
|
36
|
-
)
|
|
37
|
-
update_at: LocalDateTime = Field(
|
|
38
|
-
default_factory=datetime.datetime.now,
|
|
39
|
-
title="UPDATE_AT",
|
|
40
|
-
sa_type=DateTime,
|
|
41
|
-
sa_column_kwargs={"default": func.now(), "onupdate": func.now(), "nullable": False, "comment": "UPDATE_AT"},
|
|
42
|
-
schema_extra={"json_schema_extra": {"readOnly": True}},
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class LocalPK(SQLBase):
|
|
47
|
-
id: Cursor | None = Field(
|
|
48
|
-
default_factory=_Cursor(get_machine_seed()).next_val, title="ID", primary_key=True, sa_type=BigInteger
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class Deleted(SQLBase):
|
|
53
|
-
deleted: int = Field(
|
|
54
|
-
default=0,
|
|
55
|
-
title="DELETED",
|
|
56
|
-
sa_type=SmallInteger,
|
|
57
|
-
sa_column_kwargs={"nullable": False, "comment": "DELETED"},
|
|
58
|
-
schema_extra={"json_schema_extra": {"readOnly": True}},
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
class Versioned(SQLBase):
|
|
63
|
-
version_id: int = Field(
|
|
64
|
-
default=0,
|
|
65
|
-
title="VERSION_ID",
|
|
66
|
-
sa_type=Integer,
|
|
67
|
-
sa_column_kwargs={"nullable": False, "comment": "VERSION_ID"},
|
|
68
|
-
schema_extra={"json_schema_extra": {"readOnly": True}},
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
@declared_attr # type: ignore
|
|
72
|
-
def __mapper_args__(cls) -> dict:
|
|
73
|
-
return {"version_id_col": "version_id"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
fastapi_extra/__init__.py,sha256=DgMHXQo8iKj_jrge574Zb0iq9_zNmPKUWIVaigBvu5I,310
|
|
2
|
-
fastapi_extra/cursor.cp312-win_amd64.pyd,sha256=c0rgFuCuy2msqHfTQxI7Nk64UvgnTrr0qAQpPyd_hzM,57344
|
|
3
|
-
fastapi_extra/databases.py,sha256=KNMQUGn7ABsZfk7_XfkqNZ1zP8IkP1JoJf2MQxtmaBo,2639
|
|
4
|
-
fastapi_extra/form.py,sha256=LikaJkA16dSRGCqX9K7z2S8-e3SJcMiVXX5nRZU_kVY,957
|
|
5
|
-
fastapi_extra/model.py,sha256=_I-cp_E4Vm1D73OKGqNImiUNuKl9DvkhiONrGt5DEZg,2306
|
|
6
|
-
fastapi_extra/response.py,sha256=DHvhOSgwot5eBNKuI_jPYxZ5rshZ55Xkg-FNBJlHD1E,9609
|
|
7
|
-
fastapi_extra/routing.cp312-win_amd64.pyd,sha256=dCFyilewdtYWCBPulIWER6kWus3fntLBGJRaJzMFqMc,115200
|
|
8
|
-
fastapi_extra/settings.py,sha256=gegO1r-lV3rO_qauk8J9L5qikn1ffKAp-Z9luyMv5iQ,1118
|
|
9
|
-
fastapi_extra/types.py,sha256=FFX5831Bkjy5DGkgpiScAZuOv5AqrI58ReuP3aRvtp8,421
|
|
10
|
-
fastapi_extra/utils.py,sha256=tsPX3kpF_P5D9Bd3gnlG6rkVsLkv5gbxjml-s6ZL_6I,346
|
|
11
|
-
fastapi_extra/native/cursor.pyx,sha256=bESprFDgk9gGjyPQ4YCSg51dov2WB6s60XrOs3r5-r0,1146
|
|
12
|
-
fastapi_extra/native/routing.pyx,sha256=Umgpiz6FMevIo7428LdRm_v2O1NHKEgXWxDqGiZUS3c,5115
|
|
13
|
-
fastapi_extra-0.1.0.dist-info/LICENSE,sha256=0vTjHDa3VDsxTT-R-sH6SpYcA2F1hKtbX9ZFZQm-EcU,1516
|
|
14
|
-
fastapi_extra-0.1.0.dist-info/METADATA,sha256=TNS1sgrLbwu85E9qVtgLcNofU16JRbtnGfJJy_HNqAk,1006
|
|
15
|
-
fastapi_extra-0.1.0.dist-info/WHEEL,sha256=pWXrJbnZSH-J-PhYmKs2XNn4DHCPNBYq965vsBJBFvA,101
|
|
16
|
-
fastapi_extra-0.1.0.dist-info/top_level.txt,sha256=B7D80bEftE2E-eSd1be2r9BWkLLMZN21dRTWpb4y4Ig,14
|
|
17
|
-
fastapi_extra-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|