fastapi-rtk 1.0.19__tar.gz → 1.0.21__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_rtk-1.0.19 → fastapi_rtk-1.0.21}/PKG-INFO +1 -1
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/__init__.py +4 -1
- fastapi_rtk-1.0.21/fastapi_rtk/_version.py +1 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/api/model_rest_api.py +202 -117
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/bases/__init__.py +2 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/bases/file_manager.py +83 -10
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/file_managers/file_manager.py +1 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/file_managers/image_manager.py +1 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/file_managers/s3_file_manager.py +51 -24
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/file_managers/s3_image_manager.py +5 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/lang/messages.pot +33 -28
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +33 -26
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +31 -26
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/setting.py +8 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/__init__.py +4 -3
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/async_task_runner.py +27 -2
- fastapi_rtk-1.0.19/fastapi_rtk/utils/prettify_dict.py → fastapi_rtk-1.0.21/fastapi_rtk/utils/formatter.py +23 -1
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/run_utils.py +3 -2
- fastapi_rtk-1.0.19/fastapi_rtk/_version.py +0 -1
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/.gitignore +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/LICENSE +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/README.md +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/api/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/api/base_api.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/apis.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/auth.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/hashers/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/hashers/utils.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/strategies/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/strategies/config.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/strategies/db.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/auth/strategies/jwt.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/generic/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/generic/column.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/generic/db.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/generic/exceptions.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/generic/filters.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/generic/interface.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/generic/model.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/generic/session.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/column.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/db.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/filters.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/interface.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/model.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/backends/sqla/session.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/bases/db.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/bases/filter.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/bases/interface.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/bases/model.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/bases/session.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/cli.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/export.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/security.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/commands/translate.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/const.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/decorators.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/types.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/cli/utils.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/config.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/const.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/db.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/decorators.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/dependencies.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/exceptions.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/fastapi_react_toolkit.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/file_managers/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/filters.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/globals.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/lang/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/lang/babel/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/lang/babel/cli.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/lang/babel/config.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/lang/babel.cfg +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/lang/lazy_text.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/manager.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/middlewares.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/mixins.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/models.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/routers.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/schemas.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/security/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/security/sqla/__init__.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/security/sqla/apis.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/security/sqla/models.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/security/sqla/security_manager.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/types.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/class_factory.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/csv_json_converter.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/deep_merge.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/extender_mixin.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/hooks.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/lazy.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/merge_schema.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/pydantic.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/self_dependencies.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/sqla.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/timezone.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/update_signature.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/use_default_when_none.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/utils/werkzeug.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/fastapi_rtk/version.py +0 -0
- {fastapi_rtk-1.0.19 → fastapi_rtk-1.0.21}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-rtk
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.21
|
|
4
4
|
Summary: A package that provides a set of tools to build a FastAPI application with a Class-Based CRUD API.
|
|
5
5
|
Project-URL: Homepage, https://codeberg.org/datatactics/fastapi-rtk
|
|
6
6
|
Project-URL: Issues, https://codeberg.org/datatactics/fastapi-rtk/issues
|
|
@@ -130,7 +130,9 @@ __all__ = [
|
|
|
130
130
|
# .bases
|
|
131
131
|
"DBQueryParams",
|
|
132
132
|
"AbstractQueryBuilder",
|
|
133
|
+
"BaseFileException",
|
|
133
134
|
"FileNotAllowedException",
|
|
135
|
+
"FileTooLargeException",
|
|
134
136
|
"AbstractFileManager",
|
|
135
137
|
"AbstractImageManager",
|
|
136
138
|
"AbstractBaseFilter",
|
|
@@ -231,13 +233,14 @@ __all__ = [
|
|
|
231
233
|
"ExtenderMixin",
|
|
232
234
|
"uuid_namegen",
|
|
233
235
|
"secure_filename",
|
|
236
|
+
"prettify_dict",
|
|
237
|
+
"format_file_size",
|
|
234
238
|
"hooks",
|
|
235
239
|
"lazy",
|
|
236
240
|
"lazy_import",
|
|
237
241
|
"lazy_self",
|
|
238
242
|
"merge_schema",
|
|
239
243
|
"multiple_async_contexts",
|
|
240
|
-
"prettify_dict",
|
|
241
244
|
"generate_schema_from_typed_dict",
|
|
242
245
|
"get_pydantic_model_field",
|
|
243
246
|
"smart_run",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.21"
|
|
@@ -47,6 +47,7 @@ from ..utils import (
|
|
|
47
47
|
SelfType,
|
|
48
48
|
T,
|
|
49
49
|
deep_merge,
|
|
50
|
+
format_file_size,
|
|
50
51
|
lazy_self,
|
|
51
52
|
merge_schema,
|
|
52
53
|
smart_run,
|
|
@@ -1681,29 +1682,36 @@ class ModelRestApi(BaseApi):
|
|
|
1681
1682
|
If you are overriding this method, make sure to copy all the decorators too.
|
|
1682
1683
|
"""
|
|
1683
1684
|
async with AsyncTaskRunner():
|
|
1684
|
-
async with AsyncTaskRunner(
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
item
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1685
|
+
async with AsyncTaskRunner(
|
|
1686
|
+
"after_commit", run_tasks_even_if_exception=True
|
|
1687
|
+
) as after_commit_runner:
|
|
1688
|
+
async with AsyncTaskRunner("before_commit") as before_commit_runner:
|
|
1689
|
+
body_json = await smart_run(
|
|
1690
|
+
self._process_body,
|
|
1691
|
+
session,
|
|
1692
|
+
body,
|
|
1693
|
+
self.add_query_rel_fields,
|
|
1694
|
+
self.add_schema_extra_fields.keys()
|
|
1695
|
+
if self.add_schema_extra_fields
|
|
1696
|
+
else None,
|
|
1697
|
+
)
|
|
1698
|
+
item = self.datamodel.obj(**body_json)
|
|
1699
|
+
pre_add = await smart_run(
|
|
1700
|
+
self.pre_add,
|
|
1701
|
+
item,
|
|
1702
|
+
PARAM_BODY_SESSION(body=body, session=session),
|
|
1703
|
+
)
|
|
1704
|
+
if pre_add is not None:
|
|
1705
|
+
if isinstance(pre_add, Model):
|
|
1706
|
+
item = pre_add
|
|
1707
|
+
else:
|
|
1708
|
+
before_commit_runner.remove_tasks_by_tag("file")
|
|
1709
|
+
after_commit_runner.remove_tasks_by_tag("file")
|
|
1710
|
+
return pre_add
|
|
1706
1711
|
item = await smart_run(self.datamodel.add, session, item)
|
|
1712
|
+
after_commit_runner.remove_tasks_by_tag(
|
|
1713
|
+
"file"
|
|
1714
|
+
) # Delete any file tasks scheduled to revert files on error
|
|
1707
1715
|
post_add = await smart_run(
|
|
1708
1716
|
self.post_add,
|
|
1709
1717
|
item,
|
|
@@ -1739,50 +1747,57 @@ class ModelRestApi(BaseApi):
|
|
|
1739
1747
|
If you are overriding this method, make sure to copy all the decorators too.
|
|
1740
1748
|
"""
|
|
1741
1749
|
async with AsyncTaskRunner():
|
|
1742
|
-
async with AsyncTaskRunner(
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
fastapi.status.HTTP_404_NOT_FOUND, ErrorCode.ITEM_NOT_FOUND
|
|
1750
|
+
async with AsyncTaskRunner(
|
|
1751
|
+
"after_commit", run_tasks_even_if_exception=True
|
|
1752
|
+
) as after_commit_runner:
|
|
1753
|
+
async with AsyncTaskRunner("before_commit") as before_commit_runner:
|
|
1754
|
+
item = await smart_run(
|
|
1755
|
+
self.datamodel.get_one,
|
|
1756
|
+
session,
|
|
1757
|
+
params={
|
|
1758
|
+
"list_columns": self.show_select_columns,
|
|
1759
|
+
"where_id": id,
|
|
1760
|
+
"filter_classes": self.base_filters,
|
|
1761
|
+
"opr_filter_classes": self.base_opr_filters,
|
|
1762
|
+
},
|
|
1756
1763
|
)
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1764
|
+
if not item:
|
|
1765
|
+
raise HTTPException(
|
|
1766
|
+
fastapi.status.HTTP_404_NOT_FOUND, ErrorCode.ITEM_NOT_FOUND
|
|
1767
|
+
)
|
|
1768
|
+
body_json = await smart_run(
|
|
1769
|
+
self._process_body,
|
|
1770
|
+
session,
|
|
1771
|
+
body,
|
|
1772
|
+
self.edit_query_rel_fields,
|
|
1773
|
+
self.edit_schema_extra_fields.keys()
|
|
1774
|
+
if self.edit_schema_extra_fields
|
|
1775
|
+
else None,
|
|
1776
|
+
item=item,
|
|
1777
|
+
)
|
|
1778
|
+
await smart_run(
|
|
1779
|
+
self.pre_update_merge,
|
|
1780
|
+
item,
|
|
1781
|
+
body_json,
|
|
1782
|
+
PARAM_BODY_SESSION(body=body, session=session),
|
|
1783
|
+
)
|
|
1784
|
+
item.update(body_json)
|
|
1785
|
+
pre_update = await smart_run(
|
|
1786
|
+
self.pre_update,
|
|
1787
|
+
item,
|
|
1788
|
+
PARAM_BODY_SESSION(body=body, session=session),
|
|
1789
|
+
)
|
|
1790
|
+
if pre_update is not None:
|
|
1791
|
+
if isinstance(pre_update, Model):
|
|
1792
|
+
item = pre_update
|
|
1793
|
+
else:
|
|
1794
|
+
before_commit_runner.remove_tasks_by_tag("file")
|
|
1795
|
+
after_commit_runner.remove_tasks_by_tag("file")
|
|
1796
|
+
return pre_update
|
|
1785
1797
|
item = await smart_run(self.datamodel.edit, session, item)
|
|
1798
|
+
after_commit_runner.remove_tasks_by_tag(
|
|
1799
|
+
"file"
|
|
1800
|
+
) # Delete any file tasks scheduled to revert files on error
|
|
1786
1801
|
post_update = await smart_run(
|
|
1787
1802
|
self.post_update,
|
|
1788
1803
|
item,
|
|
@@ -1817,60 +1832,76 @@ class ModelRestApi(BaseApi):
|
|
|
1817
1832
|
If you are overriding this method, make sure to copy all the decorators too.
|
|
1818
1833
|
"""
|
|
1819
1834
|
async with AsyncTaskRunner():
|
|
1820
|
-
async with AsyncTaskRunner(
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
fastapi.status.HTTP_404_NOT_FOUND, ErrorCode.ITEM_NOT_FOUND
|
|
1835
|
+
async with AsyncTaskRunner(
|
|
1836
|
+
"after_commit", run_tasks_even_if_exception=True
|
|
1837
|
+
) as after_commit_runner:
|
|
1838
|
+
async with AsyncTaskRunner("before_commit") as before_commit_runner:
|
|
1839
|
+
item = await smart_run(
|
|
1840
|
+
self.datamodel.get_one,
|
|
1841
|
+
session,
|
|
1842
|
+
params={
|
|
1843
|
+
"list_columns": self.show_select_columns,
|
|
1844
|
+
"where_id": id,
|
|
1845
|
+
"filter_classes": self.base_filters,
|
|
1846
|
+
"opr_filter_classes": self.base_opr_filters,
|
|
1847
|
+
},
|
|
1834
1848
|
)
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
else:
|
|
1844
|
-
return pre_delete
|
|
1845
|
-
# Delete all related files and images
|
|
1846
|
-
file_and_image_columns = [
|
|
1847
|
-
x
|
|
1848
|
-
for x in self.datamodel.get_file_column_list()
|
|
1849
|
-
+ self.datamodel.get_image_column_list()
|
|
1850
|
-
]
|
|
1851
|
-
schema = self.datamodel.generate_schema(
|
|
1852
|
-
file_and_image_columns,
|
|
1853
|
-
with_id=False,
|
|
1854
|
-
with_name=False,
|
|
1855
|
-
with_property=False,
|
|
1856
|
-
)
|
|
1857
|
-
schema_data = schema.model_validate(item, from_attributes=True)
|
|
1858
|
-
for column in file_and_image_columns:
|
|
1859
|
-
fm = (
|
|
1860
|
-
self.datamodel.file_manager
|
|
1861
|
-
if self.datamodel.is_file(column)
|
|
1862
|
-
else self.datamodel.image_manager
|
|
1849
|
+
if not item:
|
|
1850
|
+
raise HTTPException(
|
|
1851
|
+
fastapi.status.HTTP_404_NOT_FOUND, ErrorCode.ITEM_NOT_FOUND
|
|
1852
|
+
)
|
|
1853
|
+
pre_delete = await smart_run(
|
|
1854
|
+
self.pre_delete,
|
|
1855
|
+
item,
|
|
1856
|
+
PARAM_ID_SESSION(id=id, session=session),
|
|
1863
1857
|
)
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1858
|
+
if pre_delete is not None:
|
|
1859
|
+
if isinstance(pre_delete, Model):
|
|
1860
|
+
item = pre_delete
|
|
1861
|
+
else:
|
|
1862
|
+
return pre_delete
|
|
1863
|
+
# Delete all related files and images
|
|
1864
|
+
file_and_image_columns = [
|
|
1865
|
+
x
|
|
1866
|
+
for x in self.datamodel.get_file_column_list()
|
|
1867
|
+
+ self.datamodel.get_image_column_list()
|
|
1868
|
+
]
|
|
1869
|
+
schema = self.datamodel.generate_schema(
|
|
1870
|
+
file_and_image_columns,
|
|
1871
|
+
with_id=False,
|
|
1872
|
+
with_name=False,
|
|
1873
|
+
with_property=False,
|
|
1874
|
+
)
|
|
1875
|
+
schema_data = schema.model_validate(item, from_attributes=True)
|
|
1876
|
+
for column in file_and_image_columns:
|
|
1877
|
+
fm = (
|
|
1878
|
+
self.datamodel.file_manager
|
|
1879
|
+
if self.datamodel.is_file(column)
|
|
1880
|
+
else self.datamodel.image_manager
|
|
1872
1881
|
)
|
|
1882
|
+
filenames = getattr(schema_data, column, None) or []
|
|
1883
|
+
if not isinstance(filenames, list):
|
|
1884
|
+
filenames = [filenames]
|
|
1885
|
+
for filename in filenames:
|
|
1886
|
+
before_commit_runner.add_task(
|
|
1887
|
+
lambda fm=fm, filename=filename: smart_run(
|
|
1888
|
+
fm.delete_file, filename
|
|
1889
|
+
)
|
|
1890
|
+
)
|
|
1891
|
+
if fm.file_exists(filename):
|
|
1892
|
+
old_content = await smart_run(fm.get_file, filename)
|
|
1893
|
+
after_commit_runner.add_task(
|
|
1894
|
+
lambda fm=fm,
|
|
1895
|
+
content=old_content,
|
|
1896
|
+
filename=filename: smart_run(
|
|
1897
|
+
fm.save_content_to_file, content, filename
|
|
1898
|
+
),
|
|
1899
|
+
tags=["file"],
|
|
1900
|
+
)
|
|
1873
1901
|
await smart_run(self.datamodel.delete, session, item)
|
|
1902
|
+
after_commit_runner.remove_tasks_by_tag(
|
|
1903
|
+
"file"
|
|
1904
|
+
) # Delete any file tasks scheduled to revert files on error
|
|
1874
1905
|
post_delete = await smart_run(
|
|
1875
1906
|
self.post_delete,
|
|
1876
1907
|
item,
|
|
@@ -2371,15 +2402,35 @@ class ModelRestApi(BaseApi):
|
|
|
2371
2402
|
)
|
|
2372
2403
|
if item and hasattr(item, key) and getattr(item, key):
|
|
2373
2404
|
actual_old_filenames = getattr(item, key)
|
|
2405
|
+
before_commit_runner = AsyncTaskRunner.get_runner(
|
|
2406
|
+
"before_commit"
|
|
2407
|
+
)
|
|
2408
|
+
after_commit_runner = AsyncTaskRunner.get_runner(
|
|
2409
|
+
"after_commit"
|
|
2410
|
+
)
|
|
2374
2411
|
# Delete only the files or images that are not in the new old_filenames
|
|
2375
2412
|
for filename in actual_old_filenames:
|
|
2376
2413
|
if filename not in old_filenames:
|
|
2377
|
-
|
|
2414
|
+
before_commit_runner.add_task(
|
|
2378
2415
|
lambda fm=fm, old_filename=filename: smart_run(
|
|
2379
2416
|
fm.delete_file, old_filename
|
|
2380
2417
|
),
|
|
2381
2418
|
tags=["file"],
|
|
2382
2419
|
)
|
|
2420
|
+
if fm.file_exists(filename):
|
|
2421
|
+
old_content = await smart_run(
|
|
2422
|
+
fm.get_file, filename
|
|
2423
|
+
)
|
|
2424
|
+
after_commit_runner.add_task(
|
|
2425
|
+
lambda fm=fm,
|
|
2426
|
+
content=old_content,
|
|
2427
|
+
filename=filename: smart_run(
|
|
2428
|
+
fm.save_content_to_file,
|
|
2429
|
+
content,
|
|
2430
|
+
filename,
|
|
2431
|
+
),
|
|
2432
|
+
tags=["file"],
|
|
2433
|
+
)
|
|
2383
2434
|
|
|
2384
2435
|
new_filenames = []
|
|
2385
2436
|
# Loop through value instead of only file values so the order is maintained
|
|
@@ -2395,12 +2446,28 @@ class ModelRestApi(BaseApi):
|
|
|
2395
2446
|
# Delete existing file or image if it is being updated
|
|
2396
2447
|
if item and hasattr(item, key) and getattr(item, key):
|
|
2397
2448
|
filename = getattr(item, key)
|
|
2398
|
-
AsyncTaskRunner.
|
|
2449
|
+
before_commit_runner = AsyncTaskRunner.get_runner(
|
|
2450
|
+
"before_commit"
|
|
2451
|
+
)
|
|
2452
|
+
before_commit_runner.add_task(
|
|
2399
2453
|
lambda fm=fm, old_filename=filename: smart_run(
|
|
2400
2454
|
fm.delete_file, old_filename
|
|
2401
2455
|
),
|
|
2402
2456
|
tags=["file"],
|
|
2403
2457
|
)
|
|
2458
|
+
if fm.file_exists(filename):
|
|
2459
|
+
old_content = await smart_run(fm.get_file, filename)
|
|
2460
|
+
after_commit_runner = AsyncTaskRunner.get_runner(
|
|
2461
|
+
"after_commit"
|
|
2462
|
+
)
|
|
2463
|
+
after_commit_runner.add_task(
|
|
2464
|
+
lambda fm=fm,
|
|
2465
|
+
content=old_content,
|
|
2466
|
+
filename=filename: smart_run(
|
|
2467
|
+
fm.save_content_to_file, content, filename
|
|
2468
|
+
),
|
|
2469
|
+
tags=["file"],
|
|
2470
|
+
)
|
|
2404
2471
|
|
|
2405
2472
|
# Only process if the value exists and is not None
|
|
2406
2473
|
if value:
|
|
@@ -2501,12 +2568,30 @@ class ModelRestApi(BaseApi):
|
|
|
2501
2568
|
),
|
|
2502
2569
|
)
|
|
2503
2570
|
content = await file.read()
|
|
2504
|
-
|
|
2571
|
+
if not fm.is_file_size_allowed(content):
|
|
2572
|
+
raise HTTPWithValidationException(
|
|
2573
|
+
fastapi.status.HTTP_400_BAD_REQUEST,
|
|
2574
|
+
"bytes_too_long",
|
|
2575
|
+
"body",
|
|
2576
|
+
key,
|
|
2577
|
+
translate(
|
|
2578
|
+
"File size from '{filename}' exceeds the allowed limit {file_size_limit}.",
|
|
2579
|
+
filename=file.filename,
|
|
2580
|
+
file_size_limit=format_file_size(fm.max_file_size),
|
|
2581
|
+
),
|
|
2582
|
+
)
|
|
2583
|
+
before_commit_runner = AsyncTaskRunner.get_runner("before_commit")
|
|
2584
|
+
after_commit_runner = AsyncTaskRunner.get_runner("after_commit")
|
|
2585
|
+
before_commit_runner.add_task(
|
|
2505
2586
|
lambda fm=fm, content=content, new_name=new_name: smart_run(
|
|
2506
2587
|
fm.save_content_to_file, content, new_name
|
|
2507
2588
|
),
|
|
2508
2589
|
tags=["file"],
|
|
2509
2590
|
)
|
|
2591
|
+
after_commit_runner.add_task(
|
|
2592
|
+
lambda fm=fm, new_name=new_name: smart_run(fm.delete_file, new_name),
|
|
2593
|
+
tags=["file"],
|
|
2594
|
+
)
|
|
2510
2595
|
return new_name
|
|
2511
2596
|
|
|
2512
2597
|
"""
|
|
@@ -6,10 +6,22 @@ import fastapi
|
|
|
6
6
|
from ..exceptions import FastAPIReactToolkitException
|
|
7
7
|
from ..utils import hooks, lazy, uuid_namegen
|
|
8
8
|
|
|
9
|
-
__all__ = [
|
|
9
|
+
__all__ = [
|
|
10
|
+
"BaseFileException",
|
|
11
|
+
"FileNotAllowedException",
|
|
12
|
+
"FileTooLargeException",
|
|
13
|
+
"AbstractFileManager",
|
|
14
|
+
"AbstractImageManager",
|
|
15
|
+
]
|
|
10
16
|
|
|
11
17
|
|
|
12
|
-
class
|
|
18
|
+
class BaseFileException(FastAPIReactToolkitException):
|
|
19
|
+
"""
|
|
20
|
+
Base exception class for file-related errors.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class FileNotAllowedException(BaseFileException):
|
|
13
25
|
"""
|
|
14
26
|
Exception raised when a file is not allowed based on its extension.
|
|
15
27
|
"""
|
|
@@ -18,13 +30,25 @@ class FileNotAllowedException(FastAPIReactToolkitException):
|
|
|
18
30
|
super().__init__(f"File '{filename}' is not allowed.")
|
|
19
31
|
|
|
20
32
|
|
|
33
|
+
class FileTooLargeException(BaseFileException):
|
|
34
|
+
"""
|
|
35
|
+
Exception raised when a file exceeds the maximum allowed size.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, filename: str, max_size: int):
|
|
39
|
+
super().__init__(
|
|
40
|
+
f"File '{filename}' exceeds the maximum allowed size of {max_size} bytes."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
21
44
|
class AbstractFileManager(abc.ABC):
|
|
22
45
|
"""
|
|
23
46
|
Abstract base class for file managers.
|
|
24
47
|
"""
|
|
25
48
|
|
|
26
49
|
base_path: str = None
|
|
27
|
-
allowed_extensions: list[str] = None
|
|
50
|
+
allowed_extensions: list[str] | None = None
|
|
51
|
+
max_file_size: int | None = None
|
|
28
52
|
namegen = lazy(lambda: uuid_namegen)
|
|
29
53
|
permission = lazy(lambda: 0o755)
|
|
30
54
|
|
|
@@ -32,6 +56,7 @@ class AbstractFileManager(abc.ABC):
|
|
|
32
56
|
self,
|
|
33
57
|
base_path: str | None = None,
|
|
34
58
|
allowed_extensions: list[str] | None = None,
|
|
59
|
+
max_file_size: int | None = None,
|
|
35
60
|
namegen: typing.Callable[[str], str] | None = None,
|
|
36
61
|
permission: int | None = None,
|
|
37
62
|
):
|
|
@@ -41,6 +66,7 @@ class AbstractFileManager(abc.ABC):
|
|
|
41
66
|
Args:
|
|
42
67
|
base_path (str | None, optional): Base path for file storage. Defaults to None.
|
|
43
68
|
allowed_extensions (list[str] | None, optional): Allowed file extensions. Defaults to None.
|
|
69
|
+
max_file_size (int | None, optional): Maximum file size allowed. Defaults to None.
|
|
44
70
|
namegen (typing.Callable[[str], str] | None, optional): Callable for generating file names. Defaults to None.
|
|
45
71
|
permission (int | None, optional): File permission settings. Defaults to None.
|
|
46
72
|
|
|
@@ -51,6 +77,8 @@ class AbstractFileManager(abc.ABC):
|
|
|
51
77
|
self.base_path = base_path
|
|
52
78
|
if allowed_extensions is not None:
|
|
53
79
|
self.allowed_extensions = allowed_extensions
|
|
80
|
+
if max_file_size is not None:
|
|
81
|
+
self.max_file_size = max_file_size
|
|
54
82
|
if namegen is not None:
|
|
55
83
|
self.namegen = namegen
|
|
56
84
|
if permission is not None:
|
|
@@ -66,15 +94,26 @@ class AbstractFileManager(abc.ABC):
|
|
|
66
94
|
|
|
67
95
|
def __init_subclass__(cls):
|
|
68
96
|
# Add pre-hook to save_file and save_content_to_file to check if the file is allowed
|
|
69
|
-
def check_is_file_allowed(self, *args, **kwargs):
|
|
70
|
-
filename = None
|
|
71
|
-
if
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
filename = args[1]
|
|
75
|
-
if filename and not self.is_filename_allowed(filename):
|
|
97
|
+
def check_is_file_allowed(self: typing.Self, *args, **kwargs):
|
|
98
|
+
filename = kwargs.get("filename", args[1] if len(args) > 1 else None)
|
|
99
|
+
if not filename:
|
|
100
|
+
raise ValueError("Filename must be provided.")
|
|
101
|
+
if not self.is_filename_allowed(filename):
|
|
76
102
|
raise FileNotAllowedException(filename)
|
|
77
103
|
|
|
104
|
+
# Add pre-hook to save_file and save_content_to_file to check if the file size is allowed
|
|
105
|
+
def check_is_file_size_allowed(self: typing.Self, *args, **kwargs):
|
|
106
|
+
input = kwargs.get(
|
|
107
|
+
"file_data", args[0] if len(args) > 0 else None
|
|
108
|
+
) or kwargs.get("content", args[0] if len(args) > 0 else None)
|
|
109
|
+
filename = kwargs.get("filename", args[1] if len(args) > 1 else None)
|
|
110
|
+
if not input or not filename:
|
|
111
|
+
raise ValueError(
|
|
112
|
+
"Both filename and file data/content must be provided."
|
|
113
|
+
)
|
|
114
|
+
if not self.is_file_size_allowed(input):
|
|
115
|
+
raise FileTooLargeException(filename, self.max_file_size)
|
|
116
|
+
|
|
78
117
|
if cls.save_file is not AbstractFileManager.save_file:
|
|
79
118
|
wrapped_save_file = hooks(pre=check_is_file_allowed)(cls.save_file)
|
|
80
119
|
cls.save_file = wrapped_save_file
|
|
@@ -83,6 +122,16 @@ class AbstractFileManager(abc.ABC):
|
|
|
83
122
|
cls.save_content_to_file
|
|
84
123
|
)
|
|
85
124
|
cls.save_content_to_file = wrapped_save_content_to_file
|
|
125
|
+
if cls.save_file is not AbstractFileManager.save_file:
|
|
126
|
+
wrapped_save_file_size = hooks(pre=check_is_file_size_allowed)(
|
|
127
|
+
cls.save_file
|
|
128
|
+
)
|
|
129
|
+
cls.save_file = wrapped_save_file_size
|
|
130
|
+
if cls.save_content_to_file is not AbstractFileManager.save_content_to_file:
|
|
131
|
+
wrapped_save_content_size = hooks(pre=check_is_file_size_allowed)(
|
|
132
|
+
cls.save_content_to_file
|
|
133
|
+
)
|
|
134
|
+
cls.save_content_to_file = wrapped_save_content_size
|
|
86
135
|
|
|
87
136
|
"""
|
|
88
137
|
--------------------------------------------------------------------------------------------------------
|
|
@@ -219,6 +268,7 @@ class AbstractFileManager(abc.ABC):
|
|
|
219
268
|
return self.__class__(
|
|
220
269
|
base_path=f"{self.base_path}/{subfolder}",
|
|
221
270
|
allowed_extensions=self.allowed_extensions,
|
|
271
|
+
max_file_size=self.max_file_size,
|
|
222
272
|
namegen=self.namegen,
|
|
223
273
|
permission=self.permission,
|
|
224
274
|
*args,
|
|
@@ -243,6 +293,29 @@ class AbstractFileManager(abc.ABC):
|
|
|
243
293
|
and filename.rsplit(".", 1)[1].lower() in self.allowed_extensions
|
|
244
294
|
)
|
|
245
295
|
|
|
296
|
+
def is_file_size_allowed(self, file_size: int | fastapi.UploadFile | bytes | str):
|
|
297
|
+
"""
|
|
298
|
+
Check if a file size is allowed based on the maximum file size.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
file_size (int | fastapi.UploadFile | bytes | str): The size of the file in bytes or the file data.
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
bool: True if the file size is allowed, False otherwise.
|
|
305
|
+
"""
|
|
306
|
+
if self.max_file_size is None:
|
|
307
|
+
return True
|
|
308
|
+
|
|
309
|
+
size = 0
|
|
310
|
+
if isinstance(file_size, int):
|
|
311
|
+
size = file_size
|
|
312
|
+
elif isinstance(file_size, fastapi.UploadFile):
|
|
313
|
+
size = file_size.size or 0
|
|
314
|
+
elif isinstance(file_size, (bytes, str)):
|
|
315
|
+
size = len(file_size)
|
|
316
|
+
|
|
317
|
+
return size <= self.max_file_size
|
|
318
|
+
|
|
246
319
|
def generate_name(self, filename: str) -> str:
|
|
247
320
|
"""
|
|
248
321
|
Generates a name for the given file data.
|
|
@@ -16,6 +16,7 @@ class FileManager(AbstractFileManager):
|
|
|
16
16
|
|
|
17
17
|
base_path = lazy(lambda: Setting.UPLOAD_FOLDER)
|
|
18
18
|
allowed_extensions = lazy(lambda: Setting.FILE_ALLOWED_EXTENSIONS)
|
|
19
|
+
max_file_size = lazy(lambda: Setting.FILE_MAX_SIZE)
|
|
19
20
|
|
|
20
21
|
def post_init(self):
|
|
21
22
|
if not self.base_path:
|
|
@@ -13,6 +13,7 @@ class ImageManager(FileManager, AbstractImageManager):
|
|
|
13
13
|
|
|
14
14
|
base_path = lazy(lambda: Setting.IMG_UPLOAD_FOLDER)
|
|
15
15
|
allowed_extensions = lazy(lambda: Setting.IMG_ALLOWED_EXTENSIONS)
|
|
16
|
+
max_file_size = lazy(lambda: Setting.IMG_MAX_SIZE)
|
|
16
17
|
|
|
17
18
|
def post_init(self):
|
|
18
19
|
if not self.base_path:
|