sqladmin 0.17.0__tar.gz → 0.18.0__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.
- {sqladmin-0.17.0 → sqladmin-0.18.0}/PKG-INFO +1 -1
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/__init__.py +1 -1
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/_menu.py +11 -9
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/_queries.py +8 -6
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/ajax.py +5 -3
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/application.py +34 -35
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/authentication.py +4 -2
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/fields.py +35 -33
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/forms.py +55 -58
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/helpers.py +8 -10
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/models.py +59 -1
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/pagination.py +5 -3
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/_macros.html +33 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/create.html +2 -18
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/edit.html +8 -24
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templating.py +9 -7
- {sqladmin-0.17.0 → sqladmin-0.18.0}/.gitignore +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/LICENSE.md +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/README.md +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/pyproject.toml +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/_types.py +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/_validators.py +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/exceptions.py +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/formatters.py +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/py.typed +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/css/flatpickr.min.css +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/css/fontawesome.min.css +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/css/main.css +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/css/select2.min.css +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/css/tabler.min.css +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/js/bootstrap.min.js +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/js/flatpickr.min.js +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/js/jquery.min.js +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/js/main.js +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/js/popper.min.js +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/js/select2.full.min.js +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/js/tabler.min.js +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/webfonts/fa-brands-400.woff2 +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/webfonts/fa-regular-400.woff2 +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/statics/webfonts/fa-solid-900.woff2 +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/base.html +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/details.html +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/error.html +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/index.html +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/layout.html +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/list.html +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/login.html +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/modals/delete.html +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/modals/details_action_confirmation.html +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/templates/sqladmin/modals/list_action_confirmation.html +0 -0
- {sqladmin-0.17.0 → sqladmin-0.18.0}/sqladmin/widgets.py +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
2
4
|
|
|
3
5
|
from starlette.datastructures import URL
|
|
4
6
|
from starlette.requests import Request
|
|
@@ -8,11 +10,11 @@ if TYPE_CHECKING:
|
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class ItemMenu:
|
|
11
|
-
def __init__(self, name: str, icon:
|
|
13
|
+
def __init__(self, name: str, icon: str | None = None) -> None:
|
|
12
14
|
self.name = name
|
|
13
15
|
self.icon = icon
|
|
14
|
-
self.parent:
|
|
15
|
-
self.children:
|
|
16
|
+
self.parent: "ItemMenu" | None = None
|
|
17
|
+
self.children: list["ItemMenu"] = []
|
|
16
18
|
|
|
17
19
|
def add_child(self, item: "ItemMenu") -> None:
|
|
18
20
|
item.parent = self
|
|
@@ -27,7 +29,7 @@ class ItemMenu:
|
|
|
27
29
|
def is_active(self, request: Request) -> bool:
|
|
28
30
|
return False
|
|
29
31
|
|
|
30
|
-
def url(self, request: Request) ->
|
|
32
|
+
def url(self, request: Request) -> str | URL:
|
|
31
33
|
return "#"
|
|
32
34
|
|
|
33
35
|
@property
|
|
@@ -53,9 +55,9 @@ class CategoryMenu(ItemMenu):
|
|
|
53
55
|
class ViewMenu(ItemMenu):
|
|
54
56
|
def __init__(
|
|
55
57
|
self,
|
|
56
|
-
view:
|
|
58
|
+
view: "BaseView" | "ModelView",
|
|
57
59
|
name: str,
|
|
58
|
-
icon:
|
|
60
|
+
icon: str | None = None,
|
|
59
61
|
) -> None:
|
|
60
62
|
super().__init__(name=name, icon=icon)
|
|
61
63
|
self.view = view
|
|
@@ -69,7 +71,7 @@ class ViewMenu(ItemMenu):
|
|
|
69
71
|
def is_active(self, request: Request) -> bool:
|
|
70
72
|
return self.view.identity == request.path_params.get("identity")
|
|
71
73
|
|
|
72
|
-
def url(self, request: Request) ->
|
|
74
|
+
def url(self, request: Request) -> str | URL:
|
|
73
75
|
if self.view.is_model:
|
|
74
76
|
return request.url_for("admin:list", identity=self.view.identity)
|
|
75
77
|
return request.url_for(f"admin:{self.view.identity}")
|
|
@@ -85,7 +87,7 @@ class ViewMenu(ItemMenu):
|
|
|
85
87
|
|
|
86
88
|
class Menu:
|
|
87
89
|
def __init__(self) -> None:
|
|
88
|
-
self.items:
|
|
90
|
+
self.items: list[ItemMenu] = []
|
|
89
91
|
|
|
90
92
|
def add(self, item: ItemMenu) -> None:
|
|
91
93
|
# Only works for one-level menu
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
2
4
|
|
|
3
5
|
import anyio
|
|
4
6
|
from sqlalchemy import select
|
|
@@ -24,7 +26,7 @@ class Query:
|
|
|
24
26
|
def __init__(self, model_view: "ModelView") -> None:
|
|
25
27
|
self.model_view = model_view
|
|
26
28
|
|
|
27
|
-
def _get_to_many_stmt(self, relation: MODEL_PROPERTY, values:
|
|
29
|
+
def _get_to_many_stmt(self, relation: MODEL_PROPERTY, values: list[Any]) -> Select:
|
|
28
30
|
target = relation.mapper.class_
|
|
29
31
|
|
|
30
32
|
target_pks = get_primary_keys(target)
|
|
@@ -131,7 +133,7 @@ class Query:
|
|
|
131
133
|
setattr(obj, key, value)
|
|
132
134
|
return obj
|
|
133
135
|
|
|
134
|
-
def _update_sync(self, pk: Any, data:
|
|
136
|
+
def _update_sync(self, pk: Any, data: dict[str, Any], request: Request) -> Any:
|
|
135
137
|
stmt = self.model_view._stmt_by_identifier(pk)
|
|
136
138
|
|
|
137
139
|
with self.model_view.session_maker(expire_on_commit=False) as session:
|
|
@@ -147,7 +149,7 @@ class Query:
|
|
|
147
149
|
return obj
|
|
148
150
|
|
|
149
151
|
async def _update_async(
|
|
150
|
-
self, pk: Any, data:
|
|
152
|
+
self, pk: Any, data: dict[str, Any], request: Request
|
|
151
153
|
) -> Any:
|
|
152
154
|
stmt = self.model_view._stmt_by_identifier(pk)
|
|
153
155
|
|
|
@@ -187,7 +189,7 @@ class Query:
|
|
|
187
189
|
await session.commit()
|
|
188
190
|
await self.model_view.after_model_delete(obj, request)
|
|
189
191
|
|
|
190
|
-
def _insert_sync(self, data:
|
|
192
|
+
def _insert_sync(self, data: dict[str, Any], request: Request) -> Any:
|
|
191
193
|
obj = self.model_view.model()
|
|
192
194
|
|
|
193
195
|
with self.model_view.session_maker(expire_on_commit=False) as session:
|
|
@@ -202,7 +204,7 @@ class Query:
|
|
|
202
204
|
)
|
|
203
205
|
return obj
|
|
204
206
|
|
|
205
|
-
async def _insert_async(self, data:
|
|
207
|
+
async def _insert_async(self, data: dict[str, Any], request: Request) -> Any:
|
|
206
208
|
obj = self.model_view.model()
|
|
207
209
|
|
|
208
210
|
async with self.model_view.session_maker(expire_on_commit=False) as session:
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
2
4
|
|
|
3
5
|
from sqlalchemy import String, cast, inspect, or_, select
|
|
4
6
|
|
|
@@ -52,13 +54,13 @@ class QueryAjaxModelLoader:
|
|
|
52
54
|
|
|
53
55
|
return remote_fields
|
|
54
56
|
|
|
55
|
-
def format(self, model: type) ->
|
|
57
|
+
def format(self, model: type) -> dict[str, Any]:
|
|
56
58
|
if not model:
|
|
57
59
|
return {}
|
|
58
60
|
|
|
59
61
|
return {"id": str(get_object_identifier(model)), "text": str(model)}
|
|
60
62
|
|
|
61
|
-
async def get_list(self, term: str, limit: int = DEFAULT_PAGE_SIZE) ->
|
|
63
|
+
async def get_list(self, term: str, limit: int = DEFAULT_PAGE_SIZE) -> list[Any]:
|
|
62
64
|
stmt = select(self.model)
|
|
63
65
|
|
|
64
66
|
# no type casting to string if a ColumnAssociationProxyInstance is given
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import inspect
|
|
2
4
|
import io
|
|
3
5
|
import logging
|
|
@@ -7,12 +9,7 @@ from typing import (
|
|
|
7
9
|
Any,
|
|
8
10
|
Awaitable,
|
|
9
11
|
Callable,
|
|
10
|
-
List,
|
|
11
|
-
Optional,
|
|
12
12
|
Sequence,
|
|
13
|
-
Tuple,
|
|
14
|
-
Type,
|
|
15
|
-
Union,
|
|
16
13
|
cast,
|
|
17
14
|
no_type_check,
|
|
18
15
|
)
|
|
@@ -66,14 +63,14 @@ class BaseAdmin:
|
|
|
66
63
|
def __init__(
|
|
67
64
|
self,
|
|
68
65
|
app: Starlette,
|
|
69
|
-
engine:
|
|
70
|
-
session_maker:
|
|
66
|
+
engine: ENGINE_TYPE | None = None,
|
|
67
|
+
session_maker: sessionmaker | None = None,
|
|
71
68
|
base_url: str = "/admin",
|
|
72
69
|
title: str = "Admin",
|
|
73
|
-
logo_url:
|
|
70
|
+
logo_url: str | None = None,
|
|
74
71
|
templates_dir: str = "templates",
|
|
75
|
-
middlewares:
|
|
76
|
-
authentication_backend:
|
|
72
|
+
middlewares: Sequence[Middleware] | None = None,
|
|
73
|
+
authentication_backend: AuthenticationBackend | None = None,
|
|
77
74
|
) -> None:
|
|
78
75
|
self.app = app
|
|
79
76
|
self.engine = engine
|
|
@@ -100,7 +97,7 @@ class BaseAdmin:
|
|
|
100
97
|
|
|
101
98
|
self.admin = Starlette(middleware=middlewares)
|
|
102
99
|
self.templates = self.init_templating_engine()
|
|
103
|
-
self._views:
|
|
100
|
+
self._views: list[BaseView | ModelView] = []
|
|
104
101
|
self._menu = Menu()
|
|
105
102
|
|
|
106
103
|
def init_templating_engine(self) -> Jinja2Templates:
|
|
@@ -120,7 +117,7 @@ class BaseAdmin:
|
|
|
120
117
|
return templates
|
|
121
118
|
|
|
122
119
|
@property
|
|
123
|
-
def views(self) ->
|
|
120
|
+
def views(self) -> list[BaseView | ModelView]:
|
|
124
121
|
"""Get list of ModelView and BaseView instances lazily.
|
|
125
122
|
|
|
126
123
|
Returns:
|
|
@@ -136,7 +133,7 @@ class BaseAdmin:
|
|
|
136
133
|
|
|
137
134
|
raise HTTPException(status_code=404)
|
|
138
135
|
|
|
139
|
-
def add_view(self, view:
|
|
136
|
+
def add_view(self, view: type[ModelView] | type[BaseView]) -> None:
|
|
140
137
|
"""Add ModelView or BaseView classes to Admin.
|
|
141
138
|
This is a shortcut that will handle both `add_model_view` and `add_base_view`.
|
|
142
139
|
"""
|
|
@@ -149,10 +146,10 @@ class BaseAdmin:
|
|
|
149
146
|
|
|
150
147
|
def _find_decorated_funcs(
|
|
151
148
|
self,
|
|
152
|
-
view:
|
|
153
|
-
view_instance:
|
|
149
|
+
view: type[BaseView | ModelView],
|
|
150
|
+
view_instance: BaseView | ModelView,
|
|
154
151
|
handle_fn: Callable[
|
|
155
|
-
[MethodType,
|
|
152
|
+
[MethodType, type[BaseView | ModelView], BaseView | ModelView],
|
|
156
153
|
None,
|
|
157
154
|
],
|
|
158
155
|
) -> None:
|
|
@@ -164,8 +161,8 @@ class BaseAdmin:
|
|
|
164
161
|
def _handle_action_decorated_func(
|
|
165
162
|
self,
|
|
166
163
|
func: MethodType,
|
|
167
|
-
view:
|
|
168
|
-
view_instance:
|
|
164
|
+
view: type[BaseView | ModelView],
|
|
165
|
+
view_instance: BaseView | ModelView,
|
|
169
166
|
) -> None:
|
|
170
167
|
if hasattr(func, "_action"):
|
|
171
168
|
view_instance = cast(ModelView, view_instance)
|
|
@@ -194,8 +191,8 @@ class BaseAdmin:
|
|
|
194
191
|
def _handle_expose_decorated_func(
|
|
195
192
|
self,
|
|
196
193
|
func: MethodType,
|
|
197
|
-
view:
|
|
198
|
-
view_instance:
|
|
194
|
+
view: type[BaseView | ModelView],
|
|
195
|
+
view_instance: BaseView | ModelView,
|
|
199
196
|
) -> None:
|
|
200
197
|
if hasattr(func, "_exposed"):
|
|
201
198
|
self.admin.add_route(
|
|
@@ -208,7 +205,7 @@ class BaseAdmin:
|
|
|
208
205
|
|
|
209
206
|
view.identity = getattr(func, "_identity")
|
|
210
207
|
|
|
211
|
-
def add_model_view(self, view:
|
|
208
|
+
def add_model_view(self, view: type[ModelView]) -> None:
|
|
212
209
|
"""Add ModelView to the Admin.
|
|
213
210
|
|
|
214
211
|
???+ usage
|
|
@@ -237,7 +234,7 @@ class BaseAdmin:
|
|
|
237
234
|
self._views.append(view_instance)
|
|
238
235
|
self._build_menu(view_instance)
|
|
239
236
|
|
|
240
|
-
def add_base_view(self, view:
|
|
237
|
+
def add_base_view(self, view: type[BaseView]) -> None:
|
|
241
238
|
"""Add BaseView to the Admin.
|
|
242
239
|
|
|
243
240
|
???+ usage
|
|
@@ -265,7 +262,7 @@ class BaseAdmin:
|
|
|
265
262
|
self._views.append(view_instance)
|
|
266
263
|
self._build_menu(view_instance)
|
|
267
264
|
|
|
268
|
-
def _build_menu(self, view:
|
|
265
|
+
def _build_menu(self, view: ModelView | BaseView) -> None:
|
|
269
266
|
if view.category:
|
|
270
267
|
menu = CategoryMenu(name=view.category)
|
|
271
268
|
menu.add_child(ViewMenu(view=view, name=view.name, icon=view.icon))
|
|
@@ -338,15 +335,15 @@ class Admin(BaseAdminView):
|
|
|
338
335
|
def __init__(
|
|
339
336
|
self,
|
|
340
337
|
app: Starlette,
|
|
341
|
-
engine:
|
|
342
|
-
session_maker:
|
|
338
|
+
engine: ENGINE_TYPE | None = None,
|
|
339
|
+
session_maker: sessionmaker | "async_sessionmaker" | None = None,
|
|
343
340
|
base_url: str = "/admin",
|
|
344
341
|
title: str = "Admin",
|
|
345
|
-
logo_url:
|
|
346
|
-
middlewares:
|
|
342
|
+
logo_url: str | None = None,
|
|
343
|
+
middlewares: Sequence[Middleware] | None = None,
|
|
347
344
|
debug: bool = False,
|
|
348
345
|
templates_dir: str = "templates",
|
|
349
|
-
authentication_backend:
|
|
346
|
+
authentication_backend: AuthenticationBackend | None = None,
|
|
350
347
|
) -> None:
|
|
351
348
|
"""
|
|
352
349
|
Args:
|
|
@@ -374,7 +371,7 @@ class Admin(BaseAdminView):
|
|
|
374
371
|
|
|
375
372
|
async def http_exception(
|
|
376
373
|
request: Request, exc: Exception
|
|
377
|
-
) ->
|
|
374
|
+
) -> Response | Awaitable[Response]:
|
|
378
375
|
assert isinstance(exc, HTTPException)
|
|
379
376
|
context = {
|
|
380
377
|
"status_code": exc.status_code,
|
|
@@ -509,6 +506,7 @@ class Admin(BaseAdminView):
|
|
|
509
506
|
model_view = self._find_model_view(identity)
|
|
510
507
|
|
|
511
508
|
Form = await model_view.scaffold_form()
|
|
509
|
+
model_view._validate_form_class(model_view._form_create_rules, Form)
|
|
512
510
|
form_data = await self._handle_form_data(request)
|
|
513
511
|
form = Form(form_data)
|
|
514
512
|
|
|
@@ -559,6 +557,7 @@ class Admin(BaseAdminView):
|
|
|
559
557
|
raise HTTPException(status_code=404)
|
|
560
558
|
|
|
561
559
|
Form = await model_view.scaffold_form()
|
|
560
|
+
model_view._validate_form_class(model_view._form_edit_rules, Form)
|
|
562
561
|
context = {
|
|
563
562
|
"obj": model,
|
|
564
563
|
"model_view": model_view,
|
|
@@ -660,7 +659,7 @@ class Admin(BaseAdminView):
|
|
|
660
659
|
|
|
661
660
|
def get_save_redirect_url(
|
|
662
661
|
self, request: Request, form: FormData, model_view: ModelView, obj: Any
|
|
663
|
-
) ->
|
|
662
|
+
) -> str | URL:
|
|
664
663
|
"""
|
|
665
664
|
Get the redirect URL after a save action
|
|
666
665
|
which is triggered from create/edit page.
|
|
@@ -685,7 +684,7 @@ class Admin(BaseAdminView):
|
|
|
685
684
|
"""
|
|
686
685
|
|
|
687
686
|
form = await request.form()
|
|
688
|
-
form_data:
|
|
687
|
+
form_data: list[tuple[str, str | UploadFile]] = []
|
|
689
688
|
for key, value in form.multi_items():
|
|
690
689
|
if not isinstance(value, UploadFile):
|
|
691
690
|
form_data.append((key, value))
|
|
@@ -726,8 +725,8 @@ class Admin(BaseAdminView):
|
|
|
726
725
|
def expose(
|
|
727
726
|
path: str,
|
|
728
727
|
*,
|
|
729
|
-
methods:
|
|
730
|
-
identity:
|
|
728
|
+
methods: list[str] = ["GET"],
|
|
729
|
+
identity: str | None = None,
|
|
731
730
|
include_in_schema: bool = True,
|
|
732
731
|
) -> Callable[..., Any]:
|
|
733
732
|
"""Expose View with information."""
|
|
@@ -746,8 +745,8 @@ def expose(
|
|
|
746
745
|
|
|
747
746
|
def action(
|
|
748
747
|
name: str,
|
|
749
|
-
label:
|
|
750
|
-
confirmation_message:
|
|
748
|
+
label: str | None = None,
|
|
749
|
+
confirmation_message: str | None = None,
|
|
751
750
|
*,
|
|
752
751
|
include_in_schema: bool = True,
|
|
753
752
|
add_in_detail: bool = True,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import functools
|
|
2
4
|
import inspect
|
|
3
|
-
from typing import Any, Callable
|
|
5
|
+
from typing import Any, Callable
|
|
4
6
|
|
|
5
7
|
from starlette.middleware import Middleware
|
|
6
8
|
from starlette.requests import Request
|
|
@@ -33,7 +35,7 @@ class AuthenticationBackend:
|
|
|
33
35
|
"""
|
|
34
36
|
raise NotImplementedError()
|
|
35
37
|
|
|
36
|
-
async def authenticate(self, request: Request) ->
|
|
38
|
+
async def authenticate(self, request: Request) -> Response | bool:
|
|
37
39
|
"""Implement authenticate logic here.
|
|
38
40
|
This method will be called for each incoming request
|
|
39
41
|
to validate the authentication.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import json
|
|
2
4
|
import operator
|
|
3
|
-
from typing import Any, Callable,
|
|
5
|
+
from typing import Any, Callable, Generator
|
|
4
6
|
|
|
5
7
|
from wtforms import Form, ValidationError, fields, widgets
|
|
6
8
|
|
|
@@ -43,7 +45,7 @@ class IntervalField(fields.StringField):
|
|
|
43
45
|
A text field which stores a `datetime.timedelta` object.
|
|
44
46
|
"""
|
|
45
47
|
|
|
46
|
-
def process_formdata(self, valuelist:
|
|
48
|
+
def process_formdata(self, valuelist: list[str]) -> None:
|
|
47
49
|
if not valuelist:
|
|
48
50
|
return
|
|
49
51
|
|
|
@@ -57,19 +59,19 @@ class IntervalField(fields.StringField):
|
|
|
57
59
|
class SelectField(fields.SelectField):
|
|
58
60
|
def __init__(
|
|
59
61
|
self,
|
|
60
|
-
label:
|
|
61
|
-
validators:
|
|
62
|
+
label: str | None = None,
|
|
63
|
+
validators: list | None = None,
|
|
62
64
|
coerce: type = str,
|
|
63
|
-
choices:
|
|
65
|
+
choices: list | Callable | None = None,
|
|
64
66
|
allow_blank: bool = False,
|
|
65
|
-
blank_text:
|
|
67
|
+
blank_text: str | None = None,
|
|
66
68
|
**kwargs: Any,
|
|
67
69
|
) -> None:
|
|
68
70
|
super().__init__(label, validators, coerce, choices, **kwargs)
|
|
69
71
|
self.allow_blank = allow_blank
|
|
70
72
|
self.blank_text = blank_text or " "
|
|
71
73
|
|
|
72
|
-
def iter_choices(self) -> Generator[
|
|
74
|
+
def iter_choices(self) -> Generator[tuple[str, str, bool, dict], None, None]:
|
|
73
75
|
choices = self.choices or []
|
|
74
76
|
|
|
75
77
|
if self.allow_blank:
|
|
@@ -86,7 +88,7 @@ class SelectField(fields.SelectField):
|
|
|
86
88
|
{},
|
|
87
89
|
)
|
|
88
90
|
|
|
89
|
-
def process_formdata(self, valuelist:
|
|
91
|
+
def process_formdata(self, valuelist: list[str]) -> None:
|
|
90
92
|
if valuelist:
|
|
91
93
|
if valuelist[0] == "__None":
|
|
92
94
|
self.data = None
|
|
@@ -112,7 +114,7 @@ class JSONField(fields.TextAreaField):
|
|
|
112
114
|
else:
|
|
113
115
|
return "{}"
|
|
114
116
|
|
|
115
|
-
def process_formdata(self, valuelist:
|
|
117
|
+
def process_formdata(self, valuelist: list[str]) -> None:
|
|
116
118
|
if valuelist:
|
|
117
119
|
value = valuelist[0]
|
|
118
120
|
|
|
@@ -132,10 +134,10 @@ class QuerySelectField(fields.SelectFieldBase):
|
|
|
132
134
|
|
|
133
135
|
def __init__(
|
|
134
136
|
self,
|
|
135
|
-
data:
|
|
136
|
-
label:
|
|
137
|
-
validators:
|
|
138
|
-
get_label:
|
|
137
|
+
data: list | None = None,
|
|
138
|
+
label: str | None = None,
|
|
139
|
+
validators: list | None = None,
|
|
140
|
+
get_label: Callable | str | None = None,
|
|
139
141
|
allow_blank: bool = False,
|
|
140
142
|
blank_text: str = "",
|
|
141
143
|
**kwargs: Any,
|
|
@@ -153,11 +155,11 @@ class QuerySelectField(fields.SelectFieldBase):
|
|
|
153
155
|
|
|
154
156
|
self.allow_blank = allow_blank
|
|
155
157
|
self.blank_text = blank_text
|
|
156
|
-
self._data:
|
|
157
|
-
self._formdata:
|
|
158
|
+
self._data: tuple | None
|
|
159
|
+
self._formdata: str | list[str] | None
|
|
158
160
|
|
|
159
161
|
@property
|
|
160
|
-
def data(self) ->
|
|
162
|
+
def data(self) -> tuple | None:
|
|
161
163
|
if self._formdata is not None:
|
|
162
164
|
for pk, _ in self._select_data:
|
|
163
165
|
if pk == self._formdata:
|
|
@@ -170,7 +172,7 @@ class QuerySelectField(fields.SelectFieldBase):
|
|
|
170
172
|
self._data = data
|
|
171
173
|
self._formdata = None
|
|
172
174
|
|
|
173
|
-
def iter_choices(self) -> Generator[
|
|
175
|
+
def iter_choices(self) -> Generator[tuple[str, str, bool, dict], None, None]:
|
|
174
176
|
if self.allow_blank:
|
|
175
177
|
yield ("__None", self.blank_text, self.data is None, {})
|
|
176
178
|
|
|
@@ -186,7 +188,7 @@ class QuerySelectField(fields.SelectFieldBase):
|
|
|
186
188
|
for pk, label in self._select_data:
|
|
187
189
|
yield (pk, self.get_label(label), str(pk) == primary_key, {})
|
|
188
190
|
|
|
189
|
-
def process_formdata(self, valuelist:
|
|
191
|
+
def process_formdata(self, valuelist: list[str]) -> None:
|
|
190
192
|
if valuelist:
|
|
191
193
|
if self.allow_blank and valuelist[0] == "__None":
|
|
192
194
|
self.data = None
|
|
@@ -220,9 +222,9 @@ class QuerySelectMultipleField(QuerySelectField):
|
|
|
220
222
|
|
|
221
223
|
def __init__(
|
|
222
224
|
self,
|
|
223
|
-
data:
|
|
224
|
-
label:
|
|
225
|
-
validators:
|
|
225
|
+
data: list | None = None,
|
|
226
|
+
label: str | None = None,
|
|
227
|
+
validators: list | None = None,
|
|
226
228
|
default: Any = None,
|
|
227
229
|
**kwargs: Any,
|
|
228
230
|
) -> None:
|
|
@@ -238,11 +240,11 @@ class QuerySelectMultipleField(QuerySelectField):
|
|
|
238
240
|
"allow_blank=True does not do anything for QuerySelectMultipleField."
|
|
239
241
|
)
|
|
240
242
|
self._invalid_formdata = False
|
|
241
|
-
self._formdata:
|
|
242
|
-
self._data:
|
|
243
|
+
self._formdata: list[str] | None = None
|
|
244
|
+
self._data: tuple | None = None
|
|
243
245
|
|
|
244
246
|
@property
|
|
245
|
-
def data(self) ->
|
|
247
|
+
def data(self) -> tuple | None:
|
|
246
248
|
formdata = self._formdata
|
|
247
249
|
if formdata is not None:
|
|
248
250
|
data = []
|
|
@@ -262,7 +264,7 @@ class QuerySelectMultipleField(QuerySelectField):
|
|
|
262
264
|
self._data = data
|
|
263
265
|
self._formdata = None
|
|
264
266
|
|
|
265
|
-
def iter_choices(self) -> Generator[
|
|
267
|
+
def iter_choices(self) -> Generator[tuple[str, Any, bool, dict], None, None]:
|
|
266
268
|
if self.data is not None:
|
|
267
269
|
primary_keys = (
|
|
268
270
|
self.data
|
|
@@ -272,7 +274,7 @@ class QuerySelectMultipleField(QuerySelectField):
|
|
|
272
274
|
for pk, label in self._select_data:
|
|
273
275
|
yield (pk, self.get_label(label), pk in primary_keys, {})
|
|
274
276
|
|
|
275
|
-
def process_formdata(self, valuelist:
|
|
277
|
+
def process_formdata(self, valuelist: list[str]) -> None:
|
|
276
278
|
self._formdata = list(set(valuelist))
|
|
277
279
|
|
|
278
280
|
def pre_validate(self, form: Form) -> None:
|
|
@@ -292,8 +294,8 @@ class AjaxSelectField(fields.SelectFieldBase):
|
|
|
292
294
|
def __init__(
|
|
293
295
|
self,
|
|
294
296
|
loader: QueryAjaxModelLoader,
|
|
295
|
-
label:
|
|
296
|
-
validators:
|
|
297
|
+
label: str | None = None,
|
|
298
|
+
validators: list | None = None,
|
|
297
299
|
allow_blank: bool = False,
|
|
298
300
|
**kwargs: Any,
|
|
299
301
|
) -> None:
|
|
@@ -334,9 +336,9 @@ class AjaxSelectMultipleField(fields.SelectFieldBase):
|
|
|
334
336
|
def __init__(
|
|
335
337
|
self,
|
|
336
338
|
loader: QueryAjaxModelLoader,
|
|
337
|
-
label:
|
|
338
|
-
validators:
|
|
339
|
-
default:
|
|
339
|
+
label: str | None = None,
|
|
340
|
+
validators: list | None = None,
|
|
341
|
+
default: list | None = None,
|
|
340
342
|
allow_blank: bool = False,
|
|
341
343
|
**kwargs: Any,
|
|
342
344
|
) -> None:
|
|
@@ -344,7 +346,7 @@ class AjaxSelectMultipleField(fields.SelectFieldBase):
|
|
|
344
346
|
self.loader = loader
|
|
345
347
|
self.allow_blank = allow_blank
|
|
346
348
|
default = default or []
|
|
347
|
-
self._formdata:
|
|
349
|
+
self._formdata: set[Any] = set()
|
|
348
350
|
|
|
349
351
|
super().__init__(label, validators, default=default, **kwargs)
|
|
350
352
|
|
|
@@ -377,7 +379,7 @@ class Select2TagsField(fields.SelectField):
|
|
|
377
379
|
def process_formdata(self, valuelist: list) -> None:
|
|
378
380
|
self.data = valuelist
|
|
379
381
|
|
|
380
|
-
def process_data(self, value:
|
|
382
|
+
def process_data(self, value: list | None) -> None:
|
|
381
383
|
self.data = value or []
|
|
382
384
|
|
|
383
385
|
|