fastapi-extra 0.4.0__tar.gz → 0.5.1__tar.gz
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-0.4.0 → fastapi_extra-0.5.1}/PKG-INFO +3 -1
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/__init__.py +1 -1
- fastapi_extra-0.5.1/fastapi_extra/_patch.py +172 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/database/model.py +2 -2
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/database/service.py +9 -4
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/response.py +10 -6
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra.egg-info/PKG-INFO +3 -1
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra.egg-info/requires.txt +3 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/pyproject.toml +1 -0
- fastapi_extra-0.4.0/fastapi_extra/_patch.py +0 -121
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/LICENSE +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/README.rst +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/cache/__init__.py +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/cache/redis.py +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/cursor.pyi +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/database/__init__.py +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/database/session.py +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/database/sqlmap.py +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/dependency.py +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/form.py +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/native/cursor.pyx +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/native/routing.pyx +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/native/urlparse.pyx +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/py.typed +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/routing.pyi +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/settings.py +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/types.py +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/urlparse.pyi +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra/utils.py +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra.egg-info/SOURCES.txt +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra.egg-info/dependency_links.txt +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/fastapi_extra.egg-info/top_level.txt +0 -0
- {fastapi_extra-0.4.0 → fastapi_extra-0.5.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-extra
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: extra package for fastapi.
|
|
5
5
|
Author-email: Ziyan Yin <408856732@qq.com>
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -30,6 +30,8 @@ Provides-Extra: mysql
|
|
|
30
30
|
Requires-Dist: asyncmy; extra == "mysql"
|
|
31
31
|
Provides-Extra: pgsql
|
|
32
32
|
Requires-Dist: asyncpg; extra == "pgsql"
|
|
33
|
+
Provides-Extra: oracle
|
|
34
|
+
Requires-Dist: python-oracledb; extra == "oracle"
|
|
33
35
|
Provides-Extra: aiomysql
|
|
34
36
|
Requires-Dist: aiomysql; extra == "aiomysql"
|
|
35
37
|
Dynamic: license-file
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
__author__ = "ziyan.yin"
|
|
2
|
+
__date__ = "2026-01-13"
|
|
3
|
+
|
|
4
|
+
from typing import Any, Mapping, Sequence
|
|
5
|
+
|
|
6
|
+
from fastapi import FastAPI, params
|
|
7
|
+
from fastapi._compat import (ModelField, get_cached_model_fields,
|
|
8
|
+
lenient_issubclass, shared)
|
|
9
|
+
from fastapi.dependencies import utils
|
|
10
|
+
from pydantic import BaseModel, Json
|
|
11
|
+
from pydantic.fields import FieldInfo
|
|
12
|
+
from starlette import datastructures
|
|
13
|
+
|
|
14
|
+
from fastapi_extra import routing
|
|
15
|
+
from fastapi_extra.urlparse import parse_qsl
|
|
16
|
+
|
|
17
|
+
_ID_MAP: dict[int, bool] = {}
|
|
18
|
+
_ALIAS_MAP: dict[int, str] = {}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def install_routes(app: FastAPI) -> None:
|
|
22
|
+
routing.install(app)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def is_sequence_field(annotation: type[Any] | None) -> bool:
|
|
26
|
+
_id = id(annotation)
|
|
27
|
+
if _id not in _ID_MAP:
|
|
28
|
+
_ID_MAP[_id] = shared.field_annotation_is_sequence(annotation)
|
|
29
|
+
return _ID_MAP[_id]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def is_json_field(field_info: FieldInfo):
|
|
33
|
+
_id = id(field_info)
|
|
34
|
+
if _id not in _ID_MAP:
|
|
35
|
+
_ID_MAP[_id] = any(type(item) is Json for item in field_info.metadata)
|
|
36
|
+
return _ID_MAP[_id]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _get_multidict_value(
|
|
40
|
+
field: ModelField,
|
|
41
|
+
values: Mapping[str, Any],
|
|
42
|
+
alias: str,
|
|
43
|
+
is_json: bool,
|
|
44
|
+
is_sequence: bool
|
|
45
|
+
) -> Any:
|
|
46
|
+
if (not is_json) and is_sequence and hasattr(values, "getlist"):
|
|
47
|
+
value = values.getlist(alias) # pyright: ignore[reportAttributeAccessIssue]
|
|
48
|
+
else:
|
|
49
|
+
value = values.get(alias, None)
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
(
|
|
53
|
+
value == ""
|
|
54
|
+
and isinstance(field.field_info, params.Form)
|
|
55
|
+
)
|
|
56
|
+
or (
|
|
57
|
+
is_sequence
|
|
58
|
+
and len(value) == 0
|
|
59
|
+
)
|
|
60
|
+
):
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
return value
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def request_params_to_args(
|
|
67
|
+
fields: Sequence[ModelField],
|
|
68
|
+
received_params: Mapping[str, Any] | datastructures.QueryParams | datastructures.Headers,
|
|
69
|
+
) -> tuple[dict[str, Any], list[Any]]:
|
|
70
|
+
values: dict[str, Any] = {}
|
|
71
|
+
errors: list[dict[str, Any]] = []
|
|
72
|
+
|
|
73
|
+
if not fields:
|
|
74
|
+
return values, errors
|
|
75
|
+
|
|
76
|
+
first_field = fields[0]
|
|
77
|
+
|
|
78
|
+
is_multidict = hasattr(received_params, "getlist")
|
|
79
|
+
is_headers = isinstance(received_params, datastructures.Headers)
|
|
80
|
+
|
|
81
|
+
# =========================================================================
|
|
82
|
+
# 分支 A:单模型解析 (例如使用 Pydantic 接收整个 Query 或是 Header 结构)
|
|
83
|
+
# =========================================================================
|
|
84
|
+
if len(fields) == 1 and lenient_issubclass(
|
|
85
|
+
first_field.field_info.annotation, BaseModel
|
|
86
|
+
):
|
|
87
|
+
fields_to_extract = get_cached_model_fields(first_field.field_info.annotation)
|
|
88
|
+
default_convert_underscores = getattr(
|
|
89
|
+
first_field.field_info, "convert_underscores", True
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
params_to_process: dict[str, Any] = {}
|
|
93
|
+
processed_keys = set()
|
|
94
|
+
|
|
95
|
+
for field in fields_to_extract:
|
|
96
|
+
field_alias = utils.get_validation_alias(field)
|
|
97
|
+
alias = field_alias
|
|
98
|
+
|
|
99
|
+
if is_headers:
|
|
100
|
+
convert_underscores = getattr(
|
|
101
|
+
field.field_info, "convert_underscores", default_convert_underscores
|
|
102
|
+
)
|
|
103
|
+
if convert_underscores and alias == field.name:
|
|
104
|
+
alias = alias.replace("_", "-")
|
|
105
|
+
|
|
106
|
+
# 提前准备属性判断
|
|
107
|
+
is_json = is_json_field(field.field_info)
|
|
108
|
+
is_sequence = is_sequence_field(field.field_info.annotation)
|
|
109
|
+
|
|
110
|
+
value = _get_multidict_value(
|
|
111
|
+
field, received_params, alias=alias, is_json=is_json, is_sequence=is_sequence
|
|
112
|
+
)
|
|
113
|
+
if value is not None:
|
|
114
|
+
params_to_process[field_alias] = value
|
|
115
|
+
processed_keys.add(alias)
|
|
116
|
+
|
|
117
|
+
# 补全未在模型中显式定义的其他传入参数
|
|
118
|
+
for key in received_params.keys():
|
|
119
|
+
if key not in processed_keys:
|
|
120
|
+
if is_multidict:
|
|
121
|
+
value = received_params.getlist(key) # type: ignore
|
|
122
|
+
params_to_process[key] = value[0] if isinstance(value, list) and len(value) == 1 else value
|
|
123
|
+
else:
|
|
124
|
+
params_to_process[key] = received_params.get(key)
|
|
125
|
+
|
|
126
|
+
field_info = first_field.field_info
|
|
127
|
+
assert isinstance(field_info, params.Param), "Params must be subclasses of Param"
|
|
128
|
+
|
|
129
|
+
v_, errors_ = first_field.validate(params_to_process, values, loc=(field_info.in_.value,))
|
|
130
|
+
return {first_field.name: v_}, errors_
|
|
131
|
+
|
|
132
|
+
# =========================================================================
|
|
133
|
+
# 分支 B:多参数平铺解析 (Fast Path - 大部分常规路由走这里)
|
|
134
|
+
# =========================================================================
|
|
135
|
+
else:
|
|
136
|
+
for field in fields:
|
|
137
|
+
field_alias = utils.get_validation_alias(field)
|
|
138
|
+
field_info = field.field_info
|
|
139
|
+
|
|
140
|
+
# 属性判断预提取与缓存
|
|
141
|
+
is_json = is_json_field(field_info)
|
|
142
|
+
is_sequence = is_sequence_field(field_info.annotation)
|
|
143
|
+
|
|
144
|
+
value = _get_multidict_value(
|
|
145
|
+
field, received_params, alias=field_alias, is_json=is_json, is_sequence=is_sequence
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
assert isinstance(field_info, params.Param), "Params must be subclasses of Param"
|
|
149
|
+
loc = (field_info.in_.value, field_alias)
|
|
150
|
+
|
|
151
|
+
v_, errors_ = utils._validate_value_with_model_field(
|
|
152
|
+
field=field, value=value, values=values, loc=loc
|
|
153
|
+
)
|
|
154
|
+
if errors_:
|
|
155
|
+
errors.extend(errors_)
|
|
156
|
+
else:
|
|
157
|
+
values[field.name] = v_
|
|
158
|
+
|
|
159
|
+
return values, errors
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def query_params_init(obj: datastructures.QueryParams, *args, **kwargs) -> None:
|
|
163
|
+
value = args[0] if args else []
|
|
164
|
+
|
|
165
|
+
if isinstance(value, bytes):
|
|
166
|
+
super(datastructures.QueryParams, obj).__init__(parse_qsl(value, keep_blank_values=True), **kwargs)
|
|
167
|
+
elif isinstance(value, str):
|
|
168
|
+
super(datastructures.QueryParams, obj).__init__(parse_qsl(value.encode("latin-1"), keep_blank_values=True), **kwargs)
|
|
169
|
+
else:
|
|
170
|
+
super(datastructures.QueryParams, obj).__init__(*args, **kwargs) # type: ignore[arg-type]
|
|
171
|
+
obj._list = [(str(k), str(v)) for k, v in obj._list]
|
|
172
|
+
obj._dict = {str(k): str(v) for k, v in obj._dict.items()}
|
|
@@ -14,7 +14,7 @@ from fastapi_extra.utils import get_machine_seed
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class AutoPK(SQLModel):
|
|
17
|
-
id: int
|
|
17
|
+
id: int = Field(
|
|
18
18
|
default=None,
|
|
19
19
|
title="ID",
|
|
20
20
|
primary_key=True,
|
|
@@ -25,7 +25,7 @@ class AutoPK(SQLModel):
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class LocalPK(SQLModel):
|
|
28
|
-
id: Cursor
|
|
28
|
+
id: Cursor = Field(
|
|
29
29
|
default_factory=_Cursor(get_machine_seed()).next_val,
|
|
30
30
|
title="ID",
|
|
31
31
|
primary_key=True,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
__author__ = "ziyan.yin"
|
|
2
2
|
__date__ = "2025-01-12"
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
from contextvars import ContextVar
|
|
5
6
|
from typing import Any, Generic, Sequence, TypeVar
|
|
6
7
|
|
|
8
|
+
import anyio.to_thread
|
|
7
9
|
from sqlalchemy import ColumnExpressionArgument
|
|
8
10
|
from sqlmodel import select
|
|
9
11
|
|
|
@@ -52,15 +54,18 @@ class ModelService(AbstractService, Generic[Model], abstract=True):
|
|
|
52
54
|
return (await self.session.exec(select(self.__model__).where(*clause))).all()
|
|
53
55
|
|
|
54
56
|
async def create_model(self, **kwargs: Any) -> Model:
|
|
55
|
-
model = self.__model__.model_validate
|
|
57
|
+
model = await anyio.to_thread.run_sync(self.__model__.model_validate, kwargs)
|
|
56
58
|
self.session.add(model)
|
|
57
59
|
await self.session.flush()
|
|
58
60
|
return model
|
|
59
61
|
|
|
60
|
-
async def update_model(self, model: Model, **kwargs: Any) -> Model:
|
|
62
|
+
async def update_model(self, model: Model, _ignore_none: bool = False, **kwargs: Any) -> Model:
|
|
61
63
|
for key, value in kwargs.items():
|
|
62
|
-
if key in model.__fields__:
|
|
63
|
-
|
|
64
|
+
if key not in model.__fields__:
|
|
65
|
+
continue
|
|
66
|
+
if _ignore_none and value is None:
|
|
67
|
+
continue
|
|
68
|
+
setattr(model, key, value)
|
|
64
69
|
await self.session.flush()
|
|
65
70
|
return model
|
|
66
71
|
|
|
@@ -3,7 +3,8 @@ __date__ = "2024-12-24"
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
from enum import Enum
|
|
6
|
-
from typing import TYPE_CHECKING, Generic, Mapping
|
|
6
|
+
from typing import (TYPE_CHECKING, Generic, Mapping, NotRequired, TypedDict,
|
|
7
|
+
Unpack)
|
|
7
8
|
|
|
8
9
|
from fastapi.responses import JSONResponse
|
|
9
10
|
from pydantic import BaseModel, Field
|
|
@@ -191,22 +192,25 @@ class ResultEnum(Enum):
|
|
|
191
192
|
C0503 = ("C0503", "邮件提醒服务失败")
|
|
192
193
|
|
|
193
194
|
|
|
195
|
+
class ResponseKwargs(TypedDict):
|
|
196
|
+
status_code: NotRequired[int]
|
|
197
|
+
headers: NotRequired[Mapping[str, str] | None]
|
|
198
|
+
|
|
199
|
+
|
|
194
200
|
class APIResult(BaseModel, Generic[T]):
|
|
195
201
|
data: T | None = Field(default=None, title="返回数据")
|
|
196
202
|
|
|
197
203
|
if TYPE_CHECKING:
|
|
198
|
-
|
|
199
204
|
@classmethod
|
|
200
|
-
def ok(cls, data: T | None = None) -> "APIResult[T]":
|
|
205
|
+
def ok(cls, data: T | None = None, **response_kwargs: Unpack[ResponseKwargs]) -> "APIResult[T]":
|
|
201
206
|
return APIResult(data=data)
|
|
202
207
|
|
|
203
208
|
else:
|
|
204
|
-
|
|
205
209
|
@classmethod
|
|
206
|
-
def ok(cls, data: T | None = None) -> "APIResponse":
|
|
210
|
+
def ok(cls, data: T | None = None, status_code: int = 200, headers: Mapping[str, str] | None = None) -> "APIResponse":
|
|
207
211
|
model = cls.__new__(cls)
|
|
208
212
|
model.__dict__["data"] = data
|
|
209
|
-
return APIResponse(model)
|
|
213
|
+
return APIResponse(model, status_code=status_code, headers=headers)
|
|
210
214
|
|
|
211
215
|
|
|
212
216
|
class APIResponse(JSONResponse):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-extra
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: extra package for fastapi.
|
|
5
5
|
Author-email: Ziyan Yin <408856732@qq.com>
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -30,6 +30,8 @@ Provides-Extra: mysql
|
|
|
30
30
|
Requires-Dist: asyncmy; extra == "mysql"
|
|
31
31
|
Provides-Extra: pgsql
|
|
32
32
|
Requires-Dist: asyncpg; extra == "pgsql"
|
|
33
|
+
Provides-Extra: oracle
|
|
34
|
+
Requires-Dist: python-oracledb; extra == "oracle"
|
|
33
35
|
Provides-Extra: aiomysql
|
|
34
36
|
Requires-Dist: aiomysql; extra == "aiomysql"
|
|
35
37
|
Dynamic: license-file
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
__author__ = "ziyan.yin"
|
|
2
|
-
__date__ = "2026-01-13"
|
|
3
|
-
|
|
4
|
-
import functools
|
|
5
|
-
from typing import Any, Mapping, Sequence, Union
|
|
6
|
-
|
|
7
|
-
from fastapi import FastAPI, params
|
|
8
|
-
from fastapi._compat import (ModelField, get_cached_model_fields,
|
|
9
|
-
lenient_issubclass, shared)
|
|
10
|
-
from fastapi.dependencies import utils
|
|
11
|
-
from pydantic import BaseModel
|
|
12
|
-
from starlette import datastructures
|
|
13
|
-
|
|
14
|
-
from fastapi_extra import routing
|
|
15
|
-
from fastapi_extra.urlparse import parse_qsl
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def install_routes(app: FastAPI) -> None:
|
|
19
|
-
routing.install(app)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@functools.lru_cache
|
|
23
|
-
def is_sequence_field(annotation: type[Any]) -> bool:
|
|
24
|
-
return shared.field_annotation_is_sequence(annotation)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def request_params_to_args(
|
|
28
|
-
fields: Sequence[ModelField],
|
|
29
|
-
received_params: Union[Mapping[str, Any], datastructures.QueryParams, datastructures.Headers],
|
|
30
|
-
) -> tuple[dict[str, Any], list[Any]]:
|
|
31
|
-
values: dict[str, Any] = {}
|
|
32
|
-
errors: list[dict[str, Any]] = []
|
|
33
|
-
|
|
34
|
-
if not fields:
|
|
35
|
-
return values, errors
|
|
36
|
-
|
|
37
|
-
first_field = fields[0]
|
|
38
|
-
fields_to_extract = fields
|
|
39
|
-
single_not_embedded_field = False
|
|
40
|
-
default_convert_underscores = True
|
|
41
|
-
if len(fields) == 1 and lenient_issubclass(
|
|
42
|
-
first_field.field_info.annotation, BaseModel
|
|
43
|
-
):
|
|
44
|
-
fields_to_extract = get_cached_model_fields(first_field.field_info.annotation)
|
|
45
|
-
single_not_embedded_field = True
|
|
46
|
-
# If headers are in a Pydantic model, the way to disable convert_underscores
|
|
47
|
-
# would be with Header(convert_underscores=False) at the Pydantic model level
|
|
48
|
-
default_convert_underscores = getattr(
|
|
49
|
-
first_field.field_info, "convert_underscores", True
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
params_to_process: dict[str, Any] = {}
|
|
53
|
-
|
|
54
|
-
processed_keys = set()
|
|
55
|
-
|
|
56
|
-
for field in fields_to_extract:
|
|
57
|
-
alias = field_alias = utils.get_validation_alias(field)
|
|
58
|
-
if isinstance(received_params, datastructures.Headers):
|
|
59
|
-
# Handle fields extracted from a Pydantic Model for a header, each field
|
|
60
|
-
# doesn't have a FieldInfo of type Header with the default convert_underscores=True
|
|
61
|
-
convert_underscores = getattr(
|
|
62
|
-
field.field_info, "convert_underscores", default_convert_underscores
|
|
63
|
-
)
|
|
64
|
-
if convert_underscores and alias == field.name:
|
|
65
|
-
alias = alias.replace("_", "-")
|
|
66
|
-
value = utils._get_multidict_value(field, received_params, alias=alias)
|
|
67
|
-
if value is not None:
|
|
68
|
-
params_to_process[field_alias] = value
|
|
69
|
-
processed_keys.add(alias)
|
|
70
|
-
|
|
71
|
-
for key in received_params.keys():
|
|
72
|
-
if key not in processed_keys:
|
|
73
|
-
if hasattr(received_params, "getlist"):
|
|
74
|
-
value = received_params.getlist(key) # type: ignore
|
|
75
|
-
if isinstance(value, list) and (len(value) == 1):
|
|
76
|
-
params_to_process[key] = value[0]
|
|
77
|
-
else:
|
|
78
|
-
params_to_process[key] = value
|
|
79
|
-
else:
|
|
80
|
-
params_to_process[key] = received_params.get(key)
|
|
81
|
-
|
|
82
|
-
if single_not_embedded_field:
|
|
83
|
-
field_info = first_field.field_info
|
|
84
|
-
assert isinstance(field_info, params.Param), (
|
|
85
|
-
"Params must be subclasses of Param"
|
|
86
|
-
)
|
|
87
|
-
loc: tuple[str, ...] = (field_info.in_.value,)
|
|
88
|
-
v_, errors_ = utils._validate_value_with_model_field(
|
|
89
|
-
field=first_field, value=params_to_process, values=values, loc=loc
|
|
90
|
-
)
|
|
91
|
-
return {first_field.name: v_}, errors_
|
|
92
|
-
|
|
93
|
-
for field in fields:
|
|
94
|
-
field_alias = utils.get_validation_alias(field)
|
|
95
|
-
value = params_to_process.get(field_alias, None)
|
|
96
|
-
field_info = field.field_info
|
|
97
|
-
assert isinstance(field_info, params.Param), (
|
|
98
|
-
"Params must be subclasses of Param"
|
|
99
|
-
)
|
|
100
|
-
loc = (field_info.in_.value, field_alias)
|
|
101
|
-
v_, errors_ = utils._validate_value_with_model_field(
|
|
102
|
-
field=field, value=value, values=values, loc=loc
|
|
103
|
-
)
|
|
104
|
-
if errors_:
|
|
105
|
-
errors.extend(errors_)
|
|
106
|
-
else:
|
|
107
|
-
values[field.name] = v_
|
|
108
|
-
return values, errors
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def query_params_init(obj: datastructures.QueryParams, *args, **kwargs) -> None:
|
|
112
|
-
value = args[0] if args else []
|
|
113
|
-
|
|
114
|
-
if isinstance(value, bytes):
|
|
115
|
-
super(datastructures.QueryParams, obj).__init__(parse_qsl(value, keep_blank_values=True), **kwargs)
|
|
116
|
-
elif isinstance(value, str):
|
|
117
|
-
super(datastructures.QueryParams, obj).__init__(parse_qsl(value.encode("latin-1"), keep_blank_values=True), **kwargs)
|
|
118
|
-
else:
|
|
119
|
-
super(datastructures.QueryParams, obj).__init__(*args, **kwargs) # type: ignore[arg-type]
|
|
120
|
-
obj._list = [(str(k), str(v)) for k, v in obj._list]
|
|
121
|
-
obj._dict = {str(k): str(v) for k, v in obj._dict.items()}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|