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.
Files changed (60) hide show
  1. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/PKG-INFO +1 -1
  2. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/fastapi_sqlalchemy_toolkit/__init__.py +1 -1
  3. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/fastapi_sqlalchemy_toolkit/model_manager.py +226 -22
  4. fastapi_sqlalchemy_toolkit-0.8.0/poetry.lock +498 -0
  5. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/pyproject.toml +1 -1
  6. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/conftest.py +2 -1
  7. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/docker-compose.yml +1 -1
  8. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/models.py +26 -1
  9. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/test_public_methods.py +98 -9
  10. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/.flake8 +0 -0
  11. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/.github/workflows/docker-test.yml +0 -0
  12. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/.github/workflows/python-publish.yml +0 -0
  13. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/.gitignore +0 -0
  14. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/.pylintrc +0 -0
  15. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/AUTHORS +0 -0
  16. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/LICENSE +0 -0
  17. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/Makefile +0 -0
  18. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/README.md +0 -0
  19. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/benefits.md +0 -0
  20. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/db_validation.md +0 -0
  21. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/extension.md +0 -0
  22. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/filtering.md +0 -0
  23. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/index.md +0 -0
  24. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/benefits.md +0 -0
  25. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/db_validation.md +0 -0
  26. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/extension.md +0 -0
  27. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/filtering.md +0 -0
  28. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/index.md +0 -0
  29. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/sorting.md +0 -0
  30. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/transactions.md +0 -0
  31. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/usage.md +0 -0
  32. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/ru/utils.md +0 -0
  33. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/sorting.md +0 -0
  34. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/transactions.md +0 -0
  35. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/usage.md +0 -0
  36. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/docs/utils.md +0 -0
  37. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/.env +0 -0
  38. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/__init__.py +0 -0
  39. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/api/__init__.py +0 -0
  40. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/api/api.py +0 -0
  41. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/api/deps.py +0 -0
  42. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/api/endpoints/__init__.py +0 -0
  43. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/api/endpoints/child.py +0 -0
  44. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/config.py +0 -0
  45. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/db.py +0 -0
  46. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/main.py +0 -0
  47. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/managers.py +0 -0
  48. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/models.py +0 -0
  49. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/examples/app/schemas.py +0 -0
  50. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/fastapi_sqlalchemy_toolkit/filters.py +0 -0
  51. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/fastapi_sqlalchemy_toolkit/ordering.py +0 -0
  52. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/fastapi_sqlalchemy_toolkit/utils.py +0 -0
  53. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/mkdocs.yml +0 -0
  54. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/requirements/base.txt +0 -0
  55. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/requirements/docs.txt +0 -0
  56. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/requirements/lint.txt +0 -0
  57. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/requirements/test.txt +0 -0
  58. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/Dockerfile +0 -0
  59. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/__init__.py +0 -0
  60. {fastapi_sqlalchemy_toolkit-0.7.19 → fastapi_sqlalchemy_toolkit-0.8.0}/tests/db.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi_sqlalchemy_toolkit
3
- Version: 0.7.19
3
+ Version: 0.8.0
4
4
  Summary: FastAPI SQLAlchemy Toolkit
5
5
  Project-URL: Homepage, https://github.com/e-kondr01/fastapi-sqlalchemy-toolkit
6
6
  Author-email: Egor Kondrashov <e.kondr01@gmail.com>
@@ -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.model.id), None, options, where, **simple_filters
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[ModelT | Row]:
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[ModelT | Row]:
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[ModelT] | List[Row]:
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[ModelT] | List[Row]:
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
- # TODO: reference primary key instead of hardcode model.id
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.model.id.in_(ids))
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.model.id.in_(ids))
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
- return relationship.any(self.reverse_relationships[field_name].id.in_(value))
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
- self.fk_name_to_model[key],
1293
+ related_model,
1096
1294
  in_obj[key],
1097
- options=[load_only(self.fk_name_to_model[key].id)],
1295
+ options=[load_only(related_pk)],
1098
1296
  )
1099
1297
  if not related_object_exists:
1100
1298
  raise HTTPException(
1101
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
1299
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
1102
1300
  detail=self.get_error_msg(
1103
- self.fk_name_to_model[key].__tablename__,
1104
- ["id"],
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, **query, where=(self.model.id != in_obj.get("id"))
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.HTTP_422_UNPROCESSABLE_ENTITY,
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.model.id != in_obj.get("id")),
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.HTTP_422_UNPROCESSABLE_ENTITY,
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_(*filters, condition, self.model.id != in_obj.get("id")),
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.HTTP_422_UNPROCESSABLE_ENTITY,
1419
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
1216
1420
  detail=self.get_error_msg(
1217
1421
  related_model.__tablename__,
1218
1422
  ["id"],