fastapi-sqlalchemy-toolkit 0.7.17__tar.gz → 0.7.18__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 (59) hide show
  1. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/PKG-INFO +1 -1
  2. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/fastapi_sqlalchemy_toolkit/model_manager.py +42 -1
  3. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/pyproject.toml +1 -1
  4. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/.flake8 +0 -0
  5. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/.github/workflows/docker-test.yml +0 -0
  6. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/.github/workflows/python-publish.yml +0 -0
  7. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/.gitignore +0 -0
  8. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/.pylintrc +0 -0
  9. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/AUTHORS +0 -0
  10. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/LICENSE +0 -0
  11. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/Makefile +0 -0
  12. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/README.md +0 -0
  13. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/benefits.md +0 -0
  14. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/db_validation.md +0 -0
  15. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/extension.md +0 -0
  16. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/filtering.md +0 -0
  17. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/index.md +0 -0
  18. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/ru/benefits.md +0 -0
  19. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/ru/db_validation.md +0 -0
  20. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/ru/extension.md +0 -0
  21. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/ru/filtering.md +0 -0
  22. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/ru/index.md +0 -0
  23. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/ru/sorting.md +0 -0
  24. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/ru/transactions.md +0 -0
  25. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/ru/usage.md +0 -0
  26. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/ru/utils.md +0 -0
  27. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/sorting.md +0 -0
  28. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/transactions.md +0 -0
  29. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/usage.md +0 -0
  30. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/docs/utils.md +0 -0
  31. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/.env +0 -0
  32. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/__init__.py +0 -0
  33. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/api/__init__.py +0 -0
  34. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/api/api.py +0 -0
  35. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/api/deps.py +0 -0
  36. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/api/endpoints/__init__.py +0 -0
  37. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/api/endpoints/child.py +0 -0
  38. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/config.py +0 -0
  39. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/db.py +0 -0
  40. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/main.py +0 -0
  41. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/managers.py +0 -0
  42. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/models.py +0 -0
  43. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/examples/app/schemas.py +0 -0
  44. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/fastapi_sqlalchemy_toolkit/__init__.py +0 -0
  45. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/fastapi_sqlalchemy_toolkit/filters.py +0 -0
  46. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/fastapi_sqlalchemy_toolkit/ordering.py +0 -0
  47. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/fastapi_sqlalchemy_toolkit/utils.py +0 -0
  48. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/mkdocs.yml +0 -0
  49. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/requirements/base.txt +0 -0
  50. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/requirements/docs.txt +0 -0
  51. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/requirements/lint.txt +0 -0
  52. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/requirements/test.txt +0 -0
  53. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/tests/Dockerfile +0 -0
  54. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/tests/__init__.py +0 -0
  55. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/tests/conftest.py +0 -0
  56. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/tests/db.py +0 -0
  57. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/tests/docker-compose.yml +0 -0
  58. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/tests/models.py +0 -0
  59. {fastapi_sqlalchemy_toolkit-0.7.17 → fastapi_sqlalchemy_toolkit-0.7.18}/tests/test_public_methods.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi_sqlalchemy_toolkit
3
- Version: 0.7.17
3
+ Version: 0.7.18
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>
@@ -7,10 +7,12 @@ from fastapi_pagination.bases import BasePage
7
7
  from fastapi_pagination.ext.sqlalchemy import paginate
8
8
  from pydantic import BaseModel
9
9
  from sqlalchemy import (
10
+ Index,
10
11
  Integer,
11
12
  Row,
12
13
  String,
13
14
  UniqueConstraint,
15
+ and_,
14
16
  delete,
15
17
  func,
16
18
  insert,
@@ -65,6 +67,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
65
67
  self.fk_name_to_model: dict[str, type[ModelT]] = {}
66
68
 
67
69
  self.unique_constraints: List[List[str]] = []
70
+ self.unique_indexes: List[Index] = []
68
71
 
69
72
  if hasattr(self.model, "__table_args__"):
70
73
  for table_arg in self.model.__table_args__:
@@ -73,6 +76,8 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
73
76
  self.unique_constraints.append(table_arg.columns.keys())
74
77
  else:
75
78
  self.unique_constraints.append(table_arg._pending_colargs)
79
+ elif isinstance(table_arg, Index) and table_arg.unique is True:
80
+ self.unique_indexes.append(table_arg)
76
81
 
77
82
  self.reverse_relationships: dict[str, type[ModelT]] = {}
78
83
  self.m2m_relationships: dict[str, type[ModelT]] = {}
@@ -865,6 +870,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
865
870
  await self.handle_m2m_fields(session, in_obj)
866
871
  await self.validate_unique_fields(session, in_obj, db_obj=db_obj)
867
872
  await self.validate_unique_constraints(session, in_obj)
873
+ await self.validate_unique_indexes(session, in_obj)
868
874
  return in_obj
869
875
 
870
876
  def get_select(self, base_stmt: Select | None = None, **_kwargs: Any) -> Select:
@@ -1119,7 +1125,7 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
1119
1125
  continue
1120
1126
  attrs_to_check = {column.name: in_obj[column.name]}
1121
1127
  object_exists = await self.exists(
1122
- session=session,
1128
+ session,
1123
1129
  **attrs_to_check,
1124
1130
  where=(self.model.id != in_obj.get("id")),
1125
1131
  )
@@ -1132,6 +1138,41 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
1132
1138
  ),
1133
1139
  )
1134
1140
 
1141
+ async def validate_unique_indexes(
1142
+ self, session: AsyncSession, in_obj: ModelDict
1143
+ ) -> None:
1144
+ """
1145
+ Валидирует соблюдение уникальности индексов
1146
+ """
1147
+ for index in self.unique_indexes:
1148
+ condition = (
1149
+ index.dialect_options["postgresql"].get("where")
1150
+ if index.dialect_options.get("postgresql")
1151
+ else None
1152
+ )
1153
+ filters = []
1154
+
1155
+ for column in index.columns:
1156
+ try:
1157
+ filters.append(column == in_obj[column.name])
1158
+ except KeyError:
1159
+ if column.nullable:
1160
+ filters.append(column.is_(None))
1161
+
1162
+ object_exists = await self.exists(
1163
+ session,
1164
+ where=and_(*filters, condition, self.model.id != in_obj.get("id")),
1165
+ )
1166
+ if object_exists:
1167
+ raise HTTPException(
1168
+ status_code=status.HTTP_400_BAD_REQUEST,
1169
+ detail=(
1170
+ f"{self.model.__tablename__} с такими "
1171
+ + ", ".join([column.name for column in index.columns])
1172
+ + " уже существует."
1173
+ ),
1174
+ )
1175
+
1135
1176
  async def handle_m2m_fields(self, session: AsyncSession, in_obj: ModelDict) -> None:
1136
1177
  for field in in_obj:
1137
1178
  if field in self.m2m_relationships:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "fastapi_sqlalchemy_toolkit"
7
- version = "0.7.17"
7
+ version = "0.7.18"
8
8
  authors = [
9
9
  { name="Egor Kondrashov", email="e.kondr01@gmail.com" },
10
10
  ]