fastapi-sqlalchemy-toolkit 0.8.2.1__tar.gz → 0.8.2.3__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.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/PKG-INFO +1 -1
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/filtering.md +21 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/filtering.md +4 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/fastapi_sqlalchemy_toolkit/model_manager.py +23 -12
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/pyproject.toml +1 -1
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/tests/test_public_methods.py +116 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/.flake8 +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/.github/workflows/docker-test.yml +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/.github/workflows/python-publish.yml +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/.gitignore +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/.pylintrc +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/AUTHORS +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/LICENSE +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/Makefile +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/README.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/benefits.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/db_validation.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/extension.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/index.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/benefits.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/db_validation.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/extension.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/index.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/sorting.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/transactions.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/usage.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/utils.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/sorting.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/transactions.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/usage.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/utils.md +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/.env +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/__init__.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/api/__init__.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/api/api.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/api/deps.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/api/endpoints/__init__.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/api/endpoints/child.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/config.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/db.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/main.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/managers.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/models.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/schemas.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/fastapi_sqlalchemy_toolkit/__init__.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/fastapi_sqlalchemy_toolkit/filters.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/fastapi_sqlalchemy_toolkit/ordering.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/fastapi_sqlalchemy_toolkit/utils.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/mkdocs.yml +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/poetry.lock +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/requirements/base.txt +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/requirements/docs.txt +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/requirements/lint.txt +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/requirements/test.txt +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/tests/Dockerfile +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/tests/__init__.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/tests/conftest.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/tests/db.py +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/tests/docker-compose.yml +0 -0
- {fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/tests/models.py +0 -0
|
@@ -278,6 +278,27 @@ async def get_parents(
|
|
|
278
278
|
|
|
279
279
|
`GET /parents?title=foo&slug=bar` — both filters are applied with `AND`.
|
|
280
280
|
|
|
281
|
+
**Case 4: Expression with a falsy value** (`MyModel.column.in_(value)`, `MyModel.column.endswith(value)`, `MyModel.column.startswith(value)`)
|
|
282
|
+
|
|
283
|
+
If `value` is an empty list (`[]`) or an empty string (`""`), the filter is skipped.
|
|
284
|
+
This covers `in_([])`, `endswith("")`, `startswith("")` and similar operators.
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
@router.get("/parents")
|
|
288
|
+
async def get_parents(
|
|
289
|
+
session: Session,
|
|
290
|
+
slugs: list[str] = Query(default=[]),
|
|
291
|
+
) -> list[ParentListSchema]:
|
|
292
|
+
return await parent_manager.list(
|
|
293
|
+
session,
|
|
294
|
+
optional_where=Parent.slug.in_(slugs),
|
|
295
|
+
)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
`GET /parents` — no filter applied, all `Parent` objects are returned.
|
|
299
|
+
|
|
300
|
+
`GET /parents?slugs=foo&slugs=bar` — only `Parent` objects whose `slug` is in `['foo', 'bar']` are returned.
|
|
301
|
+
|
|
281
302
|
> **Note**: nesting of compound expressions (e.g. `(a & b) | c`) is not supported.
|
|
282
303
|
|
|
283
304
|
**Multiple expressions as separate arguments**
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/filtering.md
RENAMED
|
@@ -218,6 +218,10 @@ async def get_parents(
|
|
|
218
218
|
|
|
219
219
|
Аналогично кейсу 1 — если `value` равно `None`, фильтр пропускается.
|
|
220
220
|
|
|
221
|
+
Также если `value` является пустым списком (`[]`) или пустой строкой (`""`), фильтр пропускается.
|
|
222
|
+
Это распространяется на `in_([])`, `endswith("")`, `startswith("")` и аналогичные операторы, которые
|
|
223
|
+
не допускают передачи `None`.
|
|
224
|
+
|
|
221
225
|
```python
|
|
222
226
|
@router.get("/parents")
|
|
223
227
|
async def get_parents(
|
|
@@ -26,7 +26,7 @@ from sqlalchemy.orm import DeclarativeBase, contains_eager, load_only
|
|
|
26
26
|
from sqlalchemy.orm.attributes import InstrumentedAttribute
|
|
27
27
|
from sqlalchemy.orm.relationships import Relationship
|
|
28
28
|
from sqlalchemy.sql import Select
|
|
29
|
-
from sqlalchemy.sql.elements import Null, UnaryExpression
|
|
29
|
+
from sqlalchemy.sql.elements import BindParameter, Null, UnaryExpression
|
|
30
30
|
from sqlalchemy.sql.expression import BinaryExpression, ColumnElement
|
|
31
31
|
from sqlalchemy.sql.functions import Function
|
|
32
32
|
from sqlalchemy.sql.schema import ScalarElementColumnDefault
|
|
@@ -46,6 +46,15 @@ def sqlalchemy_model_to_dict(model: DeclarativeBase) -> dict:
|
|
|
46
46
|
return db_obj_dict
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
def _is_falsy_bind_param(right: Any) -> bool:
|
|
50
|
+
"""Return True if *right* is a BindParameter with a falsy str or list value."""
|
|
51
|
+
return (
|
|
52
|
+
isinstance(right, BindParameter)
|
|
53
|
+
and isinstance(right.value, str | list)
|
|
54
|
+
and not right.value
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
49
58
|
def _get_model_pk(model: type[DeclarativeBase]) -> InstrumentedAttribute:
|
|
50
59
|
"""Получить атрибут первичного ключа для произвольной модели."""
|
|
51
60
|
pk_columns = list(model.__table__.primary_key.columns)
|
|
@@ -1148,10 +1157,8 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
1148
1157
|
model = filter_expression.parent._identity_class
|
|
1149
1158
|
elif isinstance(filter_expression, Function):
|
|
1150
1159
|
model = filter_expression.entity_namespace
|
|
1151
|
-
elif isinstance(filter_expression, ColumnElement):
|
|
1152
|
-
model = self.model # Not supported
|
|
1153
1160
|
else:
|
|
1154
|
-
model =
|
|
1161
|
+
model = self.model # Not supported
|
|
1155
1162
|
if model != self.model:
|
|
1156
1163
|
models_to_join.add(model)
|
|
1157
1164
|
for model in models_to_join:
|
|
@@ -1238,16 +1245,20 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
1238
1245
|
@classmethod
|
|
1239
1246
|
def handle_optional_where_expression(cls, optional_where: Any) -> Any:
|
|
1240
1247
|
"""
|
|
1241
|
-
Обрабатывает одно выражение optional_where, пропуская фильтр,
|
|
1248
|
+
Обрабатывает одно выражение optional_where, пропуская фильтр,
|
|
1249
|
+
если его значение None.
|
|
1242
1250
|
|
|
1243
1251
|
Поддерживает:
|
|
1244
1252
|
1. Простые выражения вида MyModel.field == value
|
|
1245
1253
|
2. Выражения с функциями/операторами вида func.date(MyModel.field) == value
|
|
1246
1254
|
3. Составные выражения вида (expr1) & (expr2) или (expr1) | (expr2)
|
|
1247
1255
|
(без вложенности)
|
|
1256
|
+
4. Выражения с "пустым" значением вида MyModel.column.in_([]),
|
|
1257
|
+
MyModel.column.endswith(""), MyModel.column.startswith("")
|
|
1248
1258
|
|
|
1249
|
-
Если value равно None
|
|
1250
|
-
|
|
1259
|
+
Если value равно None или является пустой строкой/списком,
|
|
1260
|
+
фильтр не применяется.
|
|
1261
|
+
В составных выражениях исключаются части с такими значениями,
|
|
1251
1262
|
при этом оператор & или | сохраняется.
|
|
1252
1263
|
"""
|
|
1253
1264
|
if optional_where is None:
|
|
@@ -1259,17 +1270,17 @@ class ModelManager(Generic[ModelT, CreateSchemaT, UpdateSchemaT]):
|
|
|
1259
1270
|
clause
|
|
1260
1271
|
for clause in optional_where.clauses
|
|
1261
1272
|
if not isinstance(clause.right, Null)
|
|
1273
|
+
and not _is_falsy_bind_param(getattr(clause, "right", None))
|
|
1262
1274
|
]
|
|
1263
1275
|
if not remaining:
|
|
1264
1276
|
return None
|
|
1265
1277
|
if len(remaining) == 1:
|
|
1266
1278
|
return remaining[0]
|
|
1267
|
-
if optional_where.operator.__name__ == "and_"
|
|
1268
|
-
|
|
1269
|
-
return or_(*remaining)
|
|
1279
|
+
combiner = and_ if optional_where.operator.__name__ == "and_" else or_
|
|
1280
|
+
return combiner(*remaining)
|
|
1270
1281
|
|
|
1271
|
-
|
|
1272
|
-
if isinstance(
|
|
1282
|
+
right = getattr(optional_where, "right", None)
|
|
1283
|
+
if isinstance(right, Null) or _is_falsy_bind_param(right):
|
|
1273
1284
|
return None
|
|
1274
1285
|
return optional_where
|
|
1275
1286
|
|
|
@@ -1079,6 +1079,122 @@ async def test_list_with_optional_where_multi_all_none(session: AsyncSession):
|
|
|
1079
1079
|
assert len(parents) == 2
|
|
1080
1080
|
|
|
1081
1081
|
|
|
1082
|
+
async def test_list_with_optional_where_in_applied(session: AsyncSession):
|
|
1083
|
+
"""Case 4: in_ expression, non-empty list — filter is applied."""
|
|
1084
|
+
target_slug = "optional-where-in-slug1"
|
|
1085
|
+
await session.execute(
|
|
1086
|
+
insert(Parent),
|
|
1087
|
+
[
|
|
1088
|
+
{"title": "optional-where-in-title-1", "slug": target_slug},
|
|
1089
|
+
{"title": "optional-where-in-title-2", "slug": "optional-where-in-slug2"},
|
|
1090
|
+
],
|
|
1091
|
+
)
|
|
1092
|
+
await session.commit()
|
|
1093
|
+
|
|
1094
|
+
parents = await parent_manager.list(
|
|
1095
|
+
session=session,
|
|
1096
|
+
optional_where=Parent.slug.in_([target_slug]),
|
|
1097
|
+
)
|
|
1098
|
+
assert len(parents) == 1
|
|
1099
|
+
assert parents[0].slug == target_slug
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
async def test_list_with_optional_where_in_skipped(session: AsyncSession):
|
|
1103
|
+
"""Case 4: in_ expression, empty list — filter is skipped (all returned)."""
|
|
1104
|
+
await session.execute(
|
|
1105
|
+
insert(Parent),
|
|
1106
|
+
[
|
|
1107
|
+
{
|
|
1108
|
+
"title": "optional-where-in-skip-title-1",
|
|
1109
|
+
"slug": "optional-where-in-skip-slug1",
|
|
1110
|
+
},
|
|
1111
|
+
{
|
|
1112
|
+
"title": "optional-where-in-skip-title-2",
|
|
1113
|
+
"slug": "optional-where-in-skip-slug2",
|
|
1114
|
+
},
|
|
1115
|
+
],
|
|
1116
|
+
)
|
|
1117
|
+
await session.commit()
|
|
1118
|
+
|
|
1119
|
+
parents = await parent_manager.list(
|
|
1120
|
+
session=session,
|
|
1121
|
+
optional_where=Parent.slug.in_([]),
|
|
1122
|
+
)
|
|
1123
|
+
assert len(parents) == 2
|
|
1124
|
+
|
|
1125
|
+
|
|
1126
|
+
async def test_list_with_optional_where_endswith_applied(session: AsyncSession):
|
|
1127
|
+
"""Case 4: endswith expression, non-empty string — filter is applied."""
|
|
1128
|
+
target_slug = "optional-where-ends-slug-xyz"
|
|
1129
|
+
await session.execute(
|
|
1130
|
+
insert(Parent),
|
|
1131
|
+
[
|
|
1132
|
+
{"title": "optional-where-ends-title-1", "slug": target_slug},
|
|
1133
|
+
{
|
|
1134
|
+
"title": "optional-where-ends-title-2",
|
|
1135
|
+
"slug": "optional-where-ends-slug-abc",
|
|
1136
|
+
},
|
|
1137
|
+
],
|
|
1138
|
+
)
|
|
1139
|
+
await session.commit()
|
|
1140
|
+
|
|
1141
|
+
parents = await parent_manager.list(
|
|
1142
|
+
session=session,
|
|
1143
|
+
optional_where=Parent.slug.endswith("xyz"),
|
|
1144
|
+
)
|
|
1145
|
+
assert len(parents) == 1
|
|
1146
|
+
assert parents[0].slug == target_slug
|
|
1147
|
+
|
|
1148
|
+
|
|
1149
|
+
async def test_list_with_optional_where_endswith_skipped(session: AsyncSession):
|
|
1150
|
+
"""Case 4: endswith expression, empty string — filter is skipped (all returned)."""
|
|
1151
|
+
await session.execute(
|
|
1152
|
+
insert(Parent),
|
|
1153
|
+
[
|
|
1154
|
+
{
|
|
1155
|
+
"title": "optional-where-ends-skip-title-1",
|
|
1156
|
+
"slug": "optional-where-ends-skip-slug1",
|
|
1157
|
+
},
|
|
1158
|
+
{
|
|
1159
|
+
"title": "optional-where-ends-skip-title-2",
|
|
1160
|
+
"slug": "optional-where-ends-skip-slug2",
|
|
1161
|
+
},
|
|
1162
|
+
],
|
|
1163
|
+
)
|
|
1164
|
+
await session.commit()
|
|
1165
|
+
|
|
1166
|
+
parents = await parent_manager.list(
|
|
1167
|
+
session=session,
|
|
1168
|
+
optional_where=Parent.slug.endswith(""),
|
|
1169
|
+
)
|
|
1170
|
+
assert len(parents) == 2
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
async def test_list_with_optional_where_in_compound_one_empty(session: AsyncSession):
|
|
1174
|
+
"""Case 4 in compound: in_([]) in & expression — only non-empty filter applied."""
|
|
1175
|
+
target_title = "optional-where-compound-in-title"
|
|
1176
|
+
await session.execute(
|
|
1177
|
+
insert(Parent),
|
|
1178
|
+
[
|
|
1179
|
+
{"title": target_title, "slug": "optional-where-compound-in-slug1"},
|
|
1180
|
+
{"title": target_title, "slug": "optional-where-compound-in-slug2"},
|
|
1181
|
+
{
|
|
1182
|
+
"title": "optional-where-compound-in-other",
|
|
1183
|
+
"slug": "optional-where-compound-in-slug3",
|
|
1184
|
+
},
|
|
1185
|
+
],
|
|
1186
|
+
)
|
|
1187
|
+
await session.commit()
|
|
1188
|
+
|
|
1189
|
+
parents = await parent_manager.list(
|
|
1190
|
+
session=session,
|
|
1191
|
+
optional_where=(Parent.title == target_title) & Parent.slug.in_([]),
|
|
1192
|
+
)
|
|
1193
|
+
assert len(parents) == 2
|
|
1194
|
+
for parent in parents:
|
|
1195
|
+
assert parent.title == target_title
|
|
1196
|
+
|
|
1197
|
+
|
|
1082
1198
|
async def test_create_unique_constraint_validation(session: AsyncSession):
|
|
1083
1199
|
await parent_manager.create(
|
|
1084
1200
|
session=session,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/db_validation.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/benefits.md
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/db_validation.md
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/extension.md
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/sorting.md
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/ru/transactions.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/docs/transactions.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/api/api.py
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/api/deps.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/config.py
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/db.py
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/main.py
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/managers.py
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/models.py
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/examples/app/schemas.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/requirements/base.txt
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/requirements/docs.txt
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/requirements/lint.txt
RENAMED
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/requirements/test.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_sqlalchemy_toolkit-0.8.2.1 → fastapi_sqlalchemy_toolkit-0.8.2.3}/tests/docker-compose.yml
RENAMED
|
File without changes
|
|
File without changes
|