fastapi-sqlalchemy-toolkit 0.7.19__tar.gz → 0.8.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.
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/PKG-INFO +1 -1
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/fastapi_sqlalchemy_toolkit/__init__.py +1 -1
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/fastapi_sqlalchemy_toolkit/model_manager.py +226 -22
- fastapi_sqlalchemy_toolkit-0.8.0/poetry.lock +498 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/pyproject.toml +1 -1
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/conftest.py +2 -1
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/docker-compose.yml +1 -1
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/models.py +26 -1
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/test_public_methods.py +98 -9
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/.flake8 +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/.github/workflows/docker-test.yml +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/.github/workflows/python-publish.yml +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/.gitignore +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/.pylintrc +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/AUTHORS +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/LICENSE +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/Makefile +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/README.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/benefits.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/db_validation.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/extension.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/filtering.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/index.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/benefits.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/db_validation.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/extension.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/filtering.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/index.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/sorting.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/transactions.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/usage.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/utils.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/sorting.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/transactions.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/usage.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/utils.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/.env +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/__init__.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/api/__init__.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/api/api.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/api/deps.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/api/endpoints/__init__.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/api/endpoints/child.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/config.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/db.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/main.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/managers.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/models.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/schemas.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/fastapi_sqlalchemy_toolkit/filters.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/fastapi_sqlalchemy_toolkit/ordering.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/fastapi_sqlalchemy_toolkit/utils.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/mkdocs.yml +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/requirements/base.txt +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/requirements/docs.txt +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/requirements/lint.txt +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/requirements/test.txt +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/Dockerfile +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/__init__.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/db.py +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
from .filters import NullableQuery
|
|
2
|
-
from .model_manager import ModelManager, sqlalchemy_model_to_dict
|
|
2
|
+
from .model_manager import ModelDict, ModelManager, sqlalchemy_model_to_dict
|
|
3
3
|
from .ordering import ordering_depends
|
|
4
4
|
from .utils import CommaSepQuery, comma_sep_q_to_list, make_partial_model
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ruff: noqa: UP006
|
|
2
2
|
from collections.abc import Callable, Iterable
|
|
3
|
-
from typing import Any, Generic, List, TypeVar # noqa: UP035
|
|
3
|
+
from typing import Any, Generic, List, TypeVar, overload # noqa: UP035
|
|
4
4
|
|
|
5
5
|
from fastapi import HTTPException, status
|
|
6
6
|
from fastapi_pagination.bases import BasePage
|
|
@@ -44,6 +44,12 @@ def sqlalchemy_model_to_dict(model: DeclarativeBase) -> dict:
|
|
|
44
44
|
return db_obj_dict
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def _get_model_pk(model: type[DeclarativeBase]) -> InstrumentedAttribute:
|
|
48
|
+
"""Получить атрибут первичного ключа для произвольной модели."""
|
|
49
|
+
pk_columns = list(model.__table__.primary_key.columns)
|
|
50
|
+
return getattr(model, pk_columns[0].name)
|
|
51
|
+
|
|
52
|
+
|
|
47
53
|
class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
48
54
|
def __init__(
|
|
49
55
|
self,
|
|
@@ -61,6 +67,15 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
61
67
|
self.model = model
|
|
62
68
|
self.default_ordering = default_ordering
|
|
63
69
|
|
|
70
|
+
# Introspect primary key
|
|
71
|
+
pk_columns = list(self.model.__table__.primary_key.columns)
|
|
72
|
+
if len(pk_columns) != 1:
|
|
73
|
+
raise ValueError(
|
|
74
|
+
f"{self.model.__name__} has a composite primary key, "
|
|
75
|
+
f"which is not supported by ModelManager."
|
|
76
|
+
)
|
|
77
|
+
self.pk_column: InstrumentedAttribute = getattr(self.model, pk_columns[0].name)
|
|
78
|
+
|
|
64
79
|
# str() of FK attr to related model
|
|
65
80
|
# "parent_id": <class app.models.parent.Parent>
|
|
66
81
|
# Используется для валидации существования FK при создании/обновлении объекта
|
|
@@ -79,7 +94,11 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
79
94
|
elif isinstance(table_arg, Index) and table_arg.unique is True:
|
|
80
95
|
self.unique_indexes.append(table_arg)
|
|
81
96
|
|
|
97
|
+
# str of reverse relationship to its Model
|
|
98
|
+
# "parents": <class app.models.parent.Parent>
|
|
82
99
|
self.reverse_relationships: dict[str, type[ModelT]] = {}
|
|
100
|
+
# str of M2M relationship to its Model
|
|
101
|
+
# "parents": <class app.models.parent.Parent>
|
|
83
102
|
self.m2m_relationships: dict[str, type[ModelT]] = {}
|
|
84
103
|
# Model to related attr
|
|
85
104
|
# Parent : Child.parent
|
|
@@ -215,6 +234,32 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
215
234
|
return result.scalars().all()
|
|
216
235
|
return None
|
|
217
236
|
|
|
237
|
+
@overload
|
|
238
|
+
async def get(
|
|
239
|
+
self,
|
|
240
|
+
session: AsyncSession,
|
|
241
|
+
options: List[Any] | Any | None = ...,
|
|
242
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
243
|
+
where: Any | None = ...,
|
|
244
|
+
base_stmt: None = ...,
|
|
245
|
+
*,
|
|
246
|
+
unique: bool = ...,
|
|
247
|
+
**simple_filters: Any,
|
|
248
|
+
) -> ModelT | None: ...
|
|
249
|
+
|
|
250
|
+
@overload
|
|
251
|
+
async def get(
|
|
252
|
+
self,
|
|
253
|
+
session: AsyncSession,
|
|
254
|
+
options: List[Any] | Any | None = ...,
|
|
255
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
256
|
+
where: Any | None = ...,
|
|
257
|
+
base_stmt: Select = ...,
|
|
258
|
+
*,
|
|
259
|
+
unique: bool = ...,
|
|
260
|
+
**simple_filters: Any,
|
|
261
|
+
) -> Row | None: ...
|
|
262
|
+
|
|
218
263
|
async def get(
|
|
219
264
|
self,
|
|
220
265
|
session: AsyncSession,
|
|
@@ -261,6 +306,32 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
261
306
|
return result.scalar_one_or_none()
|
|
262
307
|
return result.first()
|
|
263
308
|
|
|
309
|
+
@overload
|
|
310
|
+
async def get_or_404(
|
|
311
|
+
self,
|
|
312
|
+
session: AsyncSession,
|
|
313
|
+
options: List[Any] | Any | None = ...,
|
|
314
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
315
|
+
where: Any | None = ...,
|
|
316
|
+
base_stmt: None = ...,
|
|
317
|
+
*,
|
|
318
|
+
unique: bool = ...,
|
|
319
|
+
**simple_filters: Any,
|
|
320
|
+
) -> ModelT: ...
|
|
321
|
+
|
|
322
|
+
@overload
|
|
323
|
+
async def get_or_404(
|
|
324
|
+
self,
|
|
325
|
+
session: AsyncSession,
|
|
326
|
+
options: List[Any] | Any | None = ...,
|
|
327
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
328
|
+
where: Any | None = ...,
|
|
329
|
+
base_stmt: Select = ...,
|
|
330
|
+
*,
|
|
331
|
+
unique: bool = ...,
|
|
332
|
+
**simple_filters: Any,
|
|
333
|
+
) -> Row: ...
|
|
334
|
+
|
|
264
335
|
async def get_or_404(
|
|
265
336
|
self,
|
|
266
337
|
session: AsyncSession,
|
|
@@ -340,7 +411,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
340
411
|
:returns: True если объект существует, иначе False
|
|
341
412
|
"""
|
|
342
413
|
stmt = self.assemble_stmt(
|
|
343
|
-
select(self.
|
|
414
|
+
select(self.pk_column), None, options, where, **simple_filters
|
|
344
415
|
)
|
|
345
416
|
result = await session.execute(stmt)
|
|
346
417
|
return result.first() is not None
|
|
@@ -388,6 +459,30 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
388
459
|
)
|
|
389
460
|
return True
|
|
390
461
|
|
|
462
|
+
@overload
|
|
463
|
+
async def paginated_filter(
|
|
464
|
+
self,
|
|
465
|
+
session: AsyncSession,
|
|
466
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
467
|
+
options: List[Any] | Any | None = ...,
|
|
468
|
+
where: Any | None = ...,
|
|
469
|
+
base_stmt: None = ...,
|
|
470
|
+
transformer: Callable | None = ...,
|
|
471
|
+
**simple_filters: Any,
|
|
472
|
+
) -> BasePage[ModelT]: ...
|
|
473
|
+
|
|
474
|
+
@overload
|
|
475
|
+
async def paginated_filter(
|
|
476
|
+
self,
|
|
477
|
+
session: AsyncSession,
|
|
478
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
479
|
+
options: List[Any] | Any | None = ...,
|
|
480
|
+
where: Any | None = ...,
|
|
481
|
+
base_stmt: Select = ...,
|
|
482
|
+
transformer: Callable | None = ...,
|
|
483
|
+
**simple_filters: Any,
|
|
484
|
+
) -> BasePage[Row]: ...
|
|
485
|
+
|
|
391
486
|
async def paginated_filter(
|
|
392
487
|
self,
|
|
393
488
|
session: AsyncSession,
|
|
@@ -397,7 +492,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
397
492
|
base_stmt: Select | None = None,
|
|
398
493
|
transformer: Callable | None = None,
|
|
399
494
|
**simple_filters: Any,
|
|
400
|
-
) -> BasePage[
|
|
495
|
+
) -> BasePage[Any]:
|
|
401
496
|
"""
|
|
402
497
|
Получение списка объектов с фильтрами и пагинацией.
|
|
403
498
|
|
|
@@ -424,6 +519,38 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
424
519
|
stmt = self.assemble_stmt(base_stmt, order_by, options, where, **simple_filters)
|
|
425
520
|
return await paginate(session, stmt, transformer=transformer)
|
|
426
521
|
|
|
522
|
+
@overload
|
|
523
|
+
async def paginated_list(
|
|
524
|
+
self,
|
|
525
|
+
session: AsyncSession,
|
|
526
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
527
|
+
filter_expressions: dict[InstrumentedAttribute | Callable, Any] | None = ...,
|
|
528
|
+
nullable_filter_expressions: (
|
|
529
|
+
dict[InstrumentedAttribute | Callable, Any] | None
|
|
530
|
+
) = ...,
|
|
531
|
+
options: List[Any] | Any | None = ...,
|
|
532
|
+
where: Any | None = ...,
|
|
533
|
+
base_stmt: None = ...,
|
|
534
|
+
transformer: Callable | None = ...,
|
|
535
|
+
**simple_filters: Any,
|
|
536
|
+
) -> BasePage[ModelT]: ...
|
|
537
|
+
|
|
538
|
+
@overload
|
|
539
|
+
async def paginated_list(
|
|
540
|
+
self,
|
|
541
|
+
session: AsyncSession,
|
|
542
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
543
|
+
filter_expressions: dict[InstrumentedAttribute | Callable, Any] | None = ...,
|
|
544
|
+
nullable_filter_expressions: (
|
|
545
|
+
dict[InstrumentedAttribute | Callable, Any] | None
|
|
546
|
+
) = ...,
|
|
547
|
+
options: List[Any] | Any | None = ...,
|
|
548
|
+
where: Any | None = ...,
|
|
549
|
+
base_stmt: Select = ...,
|
|
550
|
+
transformer: Callable | None = ...,
|
|
551
|
+
**simple_filters: Any,
|
|
552
|
+
) -> BasePage[Row]: ...
|
|
553
|
+
|
|
427
554
|
async def paginated_list(
|
|
428
555
|
self,
|
|
429
556
|
session: AsyncSession,
|
|
@@ -437,7 +564,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
437
564
|
base_stmt: Select | None = None,
|
|
438
565
|
transformer: Callable | None = None,
|
|
439
566
|
**simple_filters: Any,
|
|
440
|
-
) -> BasePage[
|
|
567
|
+
) -> BasePage[Any]:
|
|
441
568
|
"""
|
|
442
569
|
Получение списка объектов с фильтрами и пагинацией.
|
|
443
570
|
Пропускает фильтры, значения которых None.
|
|
@@ -498,6 +625,36 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
498
625
|
|
|
499
626
|
return await paginate(session, stmt, transformer=transformer)
|
|
500
627
|
|
|
628
|
+
@overload
|
|
629
|
+
async def filter(
|
|
630
|
+
self,
|
|
631
|
+
session: AsyncSession,
|
|
632
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
633
|
+
options: List[Any] | Any | None = ...,
|
|
634
|
+
where: Any | None = ...,
|
|
635
|
+
base_stmt: None = ...,
|
|
636
|
+
limit: int | None = ...,
|
|
637
|
+
offset: int | None = ...,
|
|
638
|
+
*,
|
|
639
|
+
unique: bool = ...,
|
|
640
|
+
**simple_filters: Any,
|
|
641
|
+
) -> List[ModelT]: ...
|
|
642
|
+
|
|
643
|
+
@overload
|
|
644
|
+
async def filter(
|
|
645
|
+
self,
|
|
646
|
+
session: AsyncSession,
|
|
647
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
648
|
+
options: List[Any] | Any | None = ...,
|
|
649
|
+
where: Any | None = ...,
|
|
650
|
+
base_stmt: Select = ...,
|
|
651
|
+
limit: int | None = ...,
|
|
652
|
+
offset: int | None = ...,
|
|
653
|
+
*,
|
|
654
|
+
unique: bool = ...,
|
|
655
|
+
**simple_filters: Any,
|
|
656
|
+
) -> List[Row]: ...
|
|
657
|
+
|
|
501
658
|
async def filter(
|
|
502
659
|
self,
|
|
503
660
|
session: AsyncSession,
|
|
@@ -510,7 +667,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
510
667
|
*,
|
|
511
668
|
unique: bool = False,
|
|
512
669
|
**simple_filters: Any,
|
|
513
|
-
) -> List[
|
|
670
|
+
) -> List[Any]:
|
|
514
671
|
"""
|
|
515
672
|
Получение списка объектов с фильтрами
|
|
516
673
|
|
|
@@ -554,6 +711,44 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
554
711
|
return result.scalars().all()
|
|
555
712
|
return result.all()
|
|
556
713
|
|
|
714
|
+
@overload
|
|
715
|
+
async def list(
|
|
716
|
+
self,
|
|
717
|
+
session: AsyncSession,
|
|
718
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
719
|
+
filter_expressions: dict[InstrumentedAttribute | Callable, Any] | None = ...,
|
|
720
|
+
nullable_filter_expressions: (
|
|
721
|
+
dict[InstrumentedAttribute | Callable, Any] | None
|
|
722
|
+
) = ...,
|
|
723
|
+
options: List[Any] | Any | None = ...,
|
|
724
|
+
where: Any | None = ...,
|
|
725
|
+
base_stmt: None = ...,
|
|
726
|
+
limit: int | None = ...,
|
|
727
|
+
offset: int | None = ...,
|
|
728
|
+
*,
|
|
729
|
+
unique: bool = ...,
|
|
730
|
+
**simple_filters: Any,
|
|
731
|
+
) -> List[ModelT]: ...
|
|
732
|
+
|
|
733
|
+
@overload
|
|
734
|
+
async def list(
|
|
735
|
+
self,
|
|
736
|
+
session: AsyncSession,
|
|
737
|
+
order_by: InstrumentedAttribute | UnaryExpression | None = ...,
|
|
738
|
+
filter_expressions: dict[InstrumentedAttribute | Callable, Any] | None = ...,
|
|
739
|
+
nullable_filter_expressions: (
|
|
740
|
+
dict[InstrumentedAttribute | Callable, Any] | None
|
|
741
|
+
) = ...,
|
|
742
|
+
options: List[Any] | Any | None = ...,
|
|
743
|
+
where: Any | None = ...,
|
|
744
|
+
base_stmt: Select = ...,
|
|
745
|
+
limit: int | None = ...,
|
|
746
|
+
offset: int | None = ...,
|
|
747
|
+
*,
|
|
748
|
+
unique: bool = ...,
|
|
749
|
+
**simple_filters: Any,
|
|
750
|
+
) -> List[Row]: ...
|
|
751
|
+
|
|
557
752
|
async def list(
|
|
558
753
|
self,
|
|
559
754
|
session: AsyncSession,
|
|
@@ -570,7 +765,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
570
765
|
*,
|
|
571
766
|
unique: bool = False,
|
|
572
767
|
**simple_filters: Any,
|
|
573
|
-
) -> List[
|
|
768
|
+
) -> List[Any]:
|
|
574
769
|
"""
|
|
575
770
|
Получение списка объектов с фильтрами.
|
|
576
771
|
Пропускает фильтры, значения которых None.
|
|
@@ -666,8 +861,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
666
861
|
|
|
667
862
|
:returns: количество объектов по переданным фильтрам
|
|
668
863
|
"""
|
|
669
|
-
|
|
670
|
-
stmt = select(func.count(self.model.id))
|
|
864
|
+
stmt = select(func.count(self.pk_column))
|
|
671
865
|
if where is not None:
|
|
672
866
|
stmt = stmt.where(where)
|
|
673
867
|
if simple_filters:
|
|
@@ -764,7 +958,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
764
958
|
|
|
765
959
|
stmt = update(self.model).values(update_data)
|
|
766
960
|
if ids:
|
|
767
|
-
stmt = stmt.where(self.
|
|
961
|
+
stmt = stmt.where(self.pk_column.in_(ids))
|
|
768
962
|
elif where is not None:
|
|
769
963
|
stmt = stmt.where(where)
|
|
770
964
|
if returning:
|
|
@@ -820,7 +1014,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
820
1014
|
"""
|
|
821
1015
|
stmt = delete(self.model)
|
|
822
1016
|
if ids:
|
|
823
|
-
stmt = stmt.where(self.
|
|
1017
|
+
stmt = stmt.where(self.pk_column.in_(ids))
|
|
824
1018
|
elif where is not None:
|
|
825
1019
|
stmt = stmt.where(where)
|
|
826
1020
|
await session.execute(stmt)
|
|
@@ -993,7 +1187,9 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
993
1187
|
value: Any,
|
|
994
1188
|
) -> Exists:
|
|
995
1189
|
relationship: InstrumentedAttribute = getattr(self.model, field_name)
|
|
996
|
-
|
|
1190
|
+
related_model = self.reverse_relationships[field_name]
|
|
1191
|
+
related_pk = _get_model_pk(related_model)
|
|
1192
|
+
return relationship.any(related_pk.in_(value))
|
|
997
1193
|
|
|
998
1194
|
def assemble_stmt(
|
|
999
1195
|
self,
|
|
@@ -1091,17 +1287,19 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
1091
1287
|
|
|
1092
1288
|
for key in in_obj:
|
|
1093
1289
|
if key in self.fk_name_to_model and in_obj[key] is not None:
|
|
1290
|
+
related_model = self.fk_name_to_model[key]
|
|
1291
|
+
related_pk = _get_model_pk(related_model)
|
|
1094
1292
|
related_object_exists = await session.get(
|
|
1095
|
-
|
|
1293
|
+
related_model,
|
|
1096
1294
|
in_obj[key],
|
|
1097
|
-
options=[load_only(
|
|
1295
|
+
options=[load_only(related_pk)],
|
|
1098
1296
|
)
|
|
1099
1297
|
if not related_object_exists:
|
|
1100
1298
|
raise HTTPException(
|
|
1101
|
-
status_code=status.
|
|
1299
|
+
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
1102
1300
|
detail=self.get_error_msg(
|
|
1103
|
-
|
|
1104
|
-
[
|
|
1301
|
+
related_model.__tablename__,
|
|
1302
|
+
[related_pk.name],
|
|
1105
1303
|
field_values=[in_obj[key]],
|
|
1106
1304
|
exists=False,
|
|
1107
1305
|
),
|
|
@@ -1126,11 +1324,13 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
1126
1324
|
continue
|
|
1127
1325
|
if query:
|
|
1128
1326
|
object_exists = await self.exists(
|
|
1129
|
-
session,
|
|
1327
|
+
session,
|
|
1328
|
+
**query,
|
|
1329
|
+
where=(self.pk_column != in_obj.get(self.pk_column.name)),
|
|
1130
1330
|
)
|
|
1131
1331
|
if object_exists:
|
|
1132
1332
|
raise HTTPException(
|
|
1133
|
-
status_code=status.
|
|
1333
|
+
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
1134
1334
|
detail=self.get_error_msg(
|
|
1135
1335
|
self.model.__tablename__, unique_constraint
|
|
1136
1336
|
),
|
|
@@ -1157,11 +1357,11 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
1157
1357
|
object_exists = await self.exists(
|
|
1158
1358
|
session,
|
|
1159
1359
|
**attrs_to_check,
|
|
1160
|
-
where=(self.
|
|
1360
|
+
where=(self.pk_column != in_obj.get(self.pk_column.name)),
|
|
1161
1361
|
)
|
|
1162
1362
|
if object_exists:
|
|
1163
1363
|
raise HTTPException(
|
|
1164
|
-
status_code=status.
|
|
1364
|
+
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
1165
1365
|
detail=self.get_error_msg(
|
|
1166
1366
|
self.model.__tablename__,
|
|
1167
1367
|
[column.name],
|
|
@@ -1192,7 +1392,11 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
1192
1392
|
|
|
1193
1393
|
object_exists = await self.exists(
|
|
1194
1394
|
session,
|
|
1195
|
-
where=and_(
|
|
1395
|
+
where=and_(
|
|
1396
|
+
*filters,
|
|
1397
|
+
condition,
|
|
1398
|
+
self.pk_column != in_obj.get(self.pk_column.name),
|
|
1399
|
+
),
|
|
1196
1400
|
)
|
|
1197
1401
|
if object_exists:
|
|
1198
1402
|
raise HTTPException(
|
|
@@ -1212,7 +1416,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
1212
1416
|
related_object = await session.get(related_model, related_object_id)
|
|
1213
1417
|
if not related_object:
|
|
1214
1418
|
raise HTTPException(
|
|
1215
|
-
status_code=status.
|
|
1419
|
+
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
1216
1420
|
detail=self.get_error_msg(
|
|
1217
1421
|
related_model.__tablename__,
|
|
1218
1422
|
["id"],
|