fastapi-sqlalchemy-toolkit 0.7.4.1__tar.gz → 0.7.5.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 (59) hide show
  1. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/PKG-INFO +1 -1
  2. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/ru/usage.md +7 -1
  3. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/fastapi_sqlalchemy_toolkit/model_manager.py +140 -7
  4. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/pyproject.toml +1 -1
  5. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/.flake8 +0 -0
  6. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/.github/workflows/docker-test.yml +0 -0
  7. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/.github/workflows/python-publish.yml +0 -0
  8. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/.gitignore +0 -0
  9. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/.pylintrc +0 -0
  10. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/AUTHORS +0 -0
  11. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/LICENSE +0 -0
  12. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/Makefile +0 -0
  13. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/README.md +0 -0
  14. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/benefits.md +0 -0
  15. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/db_validation.md +0 -0
  16. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/extension.md +0 -0
  17. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/filtering.md +0 -0
  18. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/index.md +0 -0
  19. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/ru/benefits.md +0 -0
  20. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/ru/db_validation.md +0 -0
  21. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/ru/extension.md +0 -0
  22. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/ru/filtering.md +0 -0
  23. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/ru/index.md +0 -0
  24. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/ru/sorting.md +0 -0
  25. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/ru/transactions.md +0 -0
  26. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/ru/utils.md +0 -0
  27. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/sorting.md +0 -0
  28. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/transactions.md +0 -0
  29. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/usage.md +0 -0
  30. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/docs/utils.md +0 -0
  31. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/.env +0 -0
  32. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/__init__.py +0 -0
  33. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/api/__init__.py +0 -0
  34. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/api/api.py +0 -0
  35. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/api/deps.py +0 -0
  36. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/api/endpoints/__init__.py +0 -0
  37. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/api/endpoints/child.py +0 -0
  38. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/config.py +0 -0
  39. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/db.py +0 -0
  40. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/main.py +0 -0
  41. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/managers.py +0 -0
  42. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/models.py +0 -0
  43. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/examples/app/schemas.py +0 -0
  44. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/fastapi_sqlalchemy_toolkit/__init__.py +0 -0
  45. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/fastapi_sqlalchemy_toolkit/filters.py +0 -0
  46. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/fastapi_sqlalchemy_toolkit/ordering.py +0 -0
  47. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/fastapi_sqlalchemy_toolkit/utils.py +0 -0
  48. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/mkdocs.yml +0 -0
  49. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/requirements/base.txt +0 -0
  50. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/requirements/docs.txt +0 -0
  51. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/requirements/lint.txt +0 -0
  52. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/requirements/test.txt +0 -0
  53. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/tests/Dockerfile +0 -0
  54. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/tests/__init__.py +0 -0
  55. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/tests/conftest.py +0 -0
  56. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/tests/db.py +0 -0
  57. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/tests/docker-compose.yml +0 -0
  58. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/tests/models.py +0 -0
  59. {fastapi_sqlalchemy_toolkit-0.7.4.1 → fastapi_sqlalchemy_toolkit-0.7.5.0}/tests/test_public_methods.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fastapi_sqlalchemy_toolkit
3
- Version: 0.7.4.1
3
+ Version: 0.7.5.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>
@@ -52,4 +52,10 @@ my_model_manager = ModelManager[MyModel, MyModelCreateSchema, MyModelUpdateSchem
52
52
  Использование методов `paginated_list` и `paginated_filter`, согласно документации
53
53
  `fastapi_pagination`, требует применения `fastapi_pagination.add_pagination`
54
54
  к приложению `FastAPI`, и использование `fastapi_pagination.Page` в типизации ответа
55
- эндпоинта FastAPI.
55
+ эндпоинта FastAPI.
56
+
57
+ Также доступны следующие методы для выполнения действий "пачкой":
58
+
59
+ - `bulk_create` - создание объектов (частично выполняет валидацию на уровне БД)
60
+ - `bulk_update` - обновление объектов (не выполняет валидацию на уровне БД)
61
+ - `bulk_delete` - удаление объектов
@@ -6,7 +6,17 @@ from fastapi import HTTPException, status
6
6
  from fastapi_pagination.bases import BasePage
7
7
  from fastapi_pagination.ext.sqlalchemy import paginate
8
8
  from pydantic import BaseModel
9
- from sqlalchemy import Integer, Row, String, UniqueConstraint, func, select
9
+ from sqlalchemy import (
10
+ Integer,
11
+ Row,
12
+ String,
13
+ UniqueConstraint,
14
+ delete,
15
+ func,
16
+ insert,
17
+ select,
18
+ update,
19
+ )
10
20
  from sqlalchemy.dialects.postgresql import BOOLEAN
11
21
  from sqlalchemy.ext.asyncio import AsyncSession
12
22
  from sqlalchemy.orm import DeclarativeBase, contains_eager, load_only
@@ -152,6 +162,48 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
152
162
  await session.refresh(db_obj, attribute_names=refresh_attribute_names)
153
163
  return db_obj
154
164
 
165
+ async def bulk_create(
166
+ self,
167
+ session: AsyncSession,
168
+ in_objs: list[CreateSchemaT],
169
+ *,
170
+ commit: bool = True,
171
+ returning: bool = True,
172
+ **attrs: Any,
173
+ ) -> list[ModelT] | None:
174
+ """
175
+ Создание экземпляров модели пачкой и сохранение в БД.
176
+
177
+ Валидация на уровне БД выполняется для каждого объекта
178
+ (могут быть ошибки, если несколько создаваемых объектов
179
+ имеют одинаковое значение для уникального поля).
180
+
181
+ :param session: сессия SQLAlchemy
182
+
183
+ :param in_objs: модели Pydantic для создания объектов
184
+
185
+ :param commit: нужно ли вызывать `session.commit()`, если используется
186
+ подход commit as you go
187
+
188
+ :param returning: нужно ли возвращать созданные объекты
189
+
190
+ :param attrs: дополнительные значения полей создаваемых экземпляров
191
+
192
+ :returns: созданные экземпляры модели или None
193
+ """
194
+ create_data = [in_obj.model_dump() | attrs for in_obj in in_objs]
195
+ for in_obj in create_data:
196
+ await self.run_db_validation(session, in_obj)
197
+
198
+ stmt = insert(self.model).values(create_data)
199
+ if returning:
200
+ stmt = stmt.returning(self.model)
201
+ result = await session.execute(stmt)
202
+ await self.save(session, commit=commit)
203
+ if returning:
204
+ return result.scalars().all()
205
+ return None
206
+
155
207
  async def get(
156
208
  self,
157
209
  session: AsyncSession,
@@ -618,6 +670,56 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
618
670
  await session.refresh(db_obj, attribute_names=refresh_attribute_names)
619
671
  return db_obj
620
672
 
673
+ async def bulk_update(
674
+ self,
675
+ session: AsyncSession,
676
+ in_obj: UpdateSchemaT | None = None,
677
+ ids: Iterable | None = None,
678
+ where: Any | None = None,
679
+ *,
680
+ commit: bool = True,
681
+ returning: bool = True,
682
+ **attrs: Any,
683
+ ) -> List[ModelT] | None:
684
+ """
685
+ Обновление экземпляров модели пачкой и сохранение в БД.
686
+ Не выполняет валидацию на уровне БД.
687
+
688
+ :param session: сессия SQLAlchemy
689
+
690
+ :param in_obj: модель Pydantic для обновления объектов
691
+
692
+ :param ids: ID обновляемых объектов (в списке, кортеже и т.д.)
693
+
694
+ :param where: фильтр для обновления объектов,
695
+ передаётся в метод .where() SQLAlchemy.
696
+ Используется, если не передан параметр :ids:
697
+
698
+ :param commit: нужно ли вызывать `session.commit()`, если используется
699
+ подход commit as you go
700
+
701
+ :param returning: нужно ли возвращать обновлённые объекты
702
+
703
+ :param attrs: дополнительные значения полей обновляемых экземпляров
704
+
705
+ :returns: обновлённые экземпляры модели или None
706
+ """
707
+ update_data = in_obj.model_dump() if in_obj else {}
708
+ update_data.update(attrs)
709
+
710
+ stmt = update(self.model).values(update_data)
711
+ if ids:
712
+ stmt = stmt.where(self.model.id.in_(ids))
713
+ elif where:
714
+ stmt = stmt.where(where)
715
+ if returning:
716
+ stmt = stmt.returning(self.model)
717
+ result = await session.execute(stmt)
718
+ await self.save(session, commit=commit)
719
+ if returning:
720
+ return result.scalars().all()
721
+ return None
722
+
621
723
  async def delete(
622
724
  self, session: AsyncSession, db_obj: ModelT, *, commit: bool = True
623
725
  ) -> ModelT:
@@ -637,6 +739,37 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
637
739
  await self.save(session, commit=commit)
638
740
  return db_obj
639
741
 
742
+ async def bulk_delete(
743
+ self,
744
+ session: AsyncSession,
745
+ ids: Iterable | None = None,
746
+ where: Any | None = None,
747
+ *,
748
+ commit: bool = True,
749
+ ) -> None:
750
+ """
751
+ Удаление экземпляров модели пачкой из БД.
752
+
753
+ :param session: сессия SQLAlchemy
754
+
755
+ :param ids: ID удаляемых объектов (в списке, кортеже и т.д.)
756
+
757
+ :param where: фильтр для удаления объектов,
758
+ передаётся в метод .where() SQLAlchemy.
759
+ Используется, если не передан параметр :ids:
760
+
761
+ :param commit: нужно ли вызывать `session.commit()`, если используется
762
+ подход commit as you go
763
+
764
+ :returns: None
765
+ """
766
+ stmt = delete(self.model)
767
+ if ids:
768
+ stmt = stmt.where(self.model.id.in_(ids))
769
+ elif where:
770
+ stmt = stmt.where(where)
771
+ await self.save(session, commit=commit)
772
+
640
773
  ##################################################################################
641
774
  # Internal methods
642
775
  ##################################################################################
@@ -779,9 +912,9 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
779
912
  if value is None:
780
913
  del filter_expressions[filter_expression]
781
914
  elif "ilike" in str(filter_expression):
782
- filter_expressions[
783
- filter_expression
784
- ] = f"%{filter_expressions[filter_expression]}%"
915
+ filter_expressions[filter_expression] = (
916
+ f"%{filter_expressions[filter_expression]}%"
917
+ )
785
918
 
786
919
  @staticmethod
787
920
  def handle_nullable_filter_expressions(
@@ -793,9 +926,9 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
793
926
  elif value is None:
794
927
  del nullable_filter_expressions[filter_expression]
795
928
  elif "ilike" in str(filter_expression):
796
- nullable_filter_expressions[
797
- filter_expression
798
- ] = f"%{nullable_filter_expressions[filter_expression]}%"
929
+ nullable_filter_expressions[filter_expression] = (
930
+ f"%{nullable_filter_expressions[filter_expression]}%"
931
+ )
799
932
 
800
933
  def get_reverse_relation_filter_stmt(
801
934
  self,
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "fastapi_sqlalchemy_toolkit"
7
- version = "0.7.4.1"
7
+ version = "0.7.5.0"
8
8
  authors = [
9
9
  { name="Egor Kondrashov", email="e.kondr01@gmail.com" },
10
10
  ]