fastapi-rtk 0.2.27__py3-none-any.whl → 1.0.13__py3-none-any.whl

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 (98) hide show
  1. fastapi_rtk/__init__.py +39 -35
  2. fastapi_rtk/_version.py +1 -0
  3. fastapi_rtk/api/model_rest_api.py +476 -221
  4. fastapi_rtk/auth/auth.py +0 -9
  5. fastapi_rtk/backends/generic/__init__.py +6 -0
  6. fastapi_rtk/backends/generic/column.py +21 -12
  7. fastapi_rtk/backends/generic/db.py +42 -7
  8. fastapi_rtk/backends/generic/filters.py +21 -16
  9. fastapi_rtk/backends/generic/interface.py +14 -8
  10. fastapi_rtk/backends/generic/model.py +19 -11
  11. fastapi_rtk/backends/sqla/__init__.py +1 -0
  12. fastapi_rtk/backends/sqla/db.py +77 -17
  13. fastapi_rtk/backends/sqla/extensions/audit/audit.py +401 -189
  14. fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +15 -12
  15. fastapi_rtk/backends/sqla/filters.py +50 -21
  16. fastapi_rtk/backends/sqla/interface.py +96 -34
  17. fastapi_rtk/backends/sqla/model.py +56 -39
  18. fastapi_rtk/bases/__init__.py +20 -0
  19. fastapi_rtk/bases/db.py +94 -7
  20. fastapi_rtk/bases/file_manager.py +47 -3
  21. fastapi_rtk/bases/filter.py +22 -0
  22. fastapi_rtk/bases/interface.py +49 -5
  23. fastapi_rtk/bases/model.py +3 -0
  24. fastapi_rtk/bases/session.py +2 -0
  25. fastapi_rtk/cli/cli.py +62 -9
  26. fastapi_rtk/cli/commands/__init__.py +23 -0
  27. fastapi_rtk/cli/{db.py → commands/db/__init__.py} +107 -50
  28. fastapi_rtk/cli/{templates → commands/db/templates}/fastapi/env.py +2 -3
  29. fastapi_rtk/cli/{templates → commands/db/templates}/fastapi-multidb/env.py +10 -9
  30. fastapi_rtk/cli/{templates → commands/db/templates}/fastapi-multidb/script.py.mako +3 -1
  31. fastapi_rtk/cli/{export.py → commands/export.py} +12 -10
  32. fastapi_rtk/cli/{security.py → commands/security.py} +73 -7
  33. fastapi_rtk/cli/commands/translate.py +299 -0
  34. fastapi_rtk/cli/decorators.py +9 -4
  35. fastapi_rtk/cli/utils.py +46 -0
  36. fastapi_rtk/config.py +41 -1
  37. fastapi_rtk/const.py +29 -1
  38. fastapi_rtk/db.py +76 -40
  39. fastapi_rtk/decorators.py +1 -1
  40. fastapi_rtk/dependencies.py +134 -62
  41. fastapi_rtk/exceptions.py +51 -1
  42. fastapi_rtk/fastapi_react_toolkit.py +186 -171
  43. fastapi_rtk/file_managers/file_manager.py +8 -6
  44. fastapi_rtk/file_managers/s3_file_manager.py +69 -33
  45. fastapi_rtk/globals.py +22 -12
  46. fastapi_rtk/lang/__init__.py +3 -0
  47. fastapi_rtk/lang/babel/__init__.py +4 -0
  48. fastapi_rtk/lang/babel/cli.py +40 -0
  49. fastapi_rtk/lang/babel/config.py +17 -0
  50. fastapi_rtk/lang/babel.cfg +1 -0
  51. fastapi_rtk/lang/lazy_text.py +120 -0
  52. fastapi_rtk/lang/messages.pot +238 -0
  53. fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
  54. fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +248 -0
  55. fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
  56. fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +244 -0
  57. fastapi_rtk/manager.py +355 -37
  58. fastapi_rtk/mixins.py +12 -0
  59. fastapi_rtk/routers.py +208 -72
  60. fastapi_rtk/schemas.py +142 -39
  61. fastapi_rtk/security/sqla/apis.py +39 -13
  62. fastapi_rtk/security/sqla/models.py +8 -23
  63. fastapi_rtk/security/sqla/security_manager.py +369 -11
  64. fastapi_rtk/setting.py +446 -88
  65. fastapi_rtk/types.py +94 -27
  66. fastapi_rtk/utils/__init__.py +8 -0
  67. fastapi_rtk/utils/async_task_runner.py +286 -61
  68. fastapi_rtk/utils/csv_json_converter.py +243 -40
  69. fastapi_rtk/utils/hooks.py +34 -0
  70. fastapi_rtk/utils/merge_schema.py +3 -3
  71. fastapi_rtk/utils/multiple_async_contexts.py +21 -0
  72. fastapi_rtk/utils/pydantic.py +46 -1
  73. fastapi_rtk/utils/run_utils.py +31 -1
  74. fastapi_rtk/utils/self_dependencies.py +1 -1
  75. fastapi_rtk/utils/use_default_when_none.py +1 -1
  76. fastapi_rtk/version.py +6 -1
  77. fastapi_rtk-1.0.13.dist-info/METADATA +28 -0
  78. fastapi_rtk-1.0.13.dist-info/RECORD +133 -0
  79. {fastapi_rtk-0.2.27.dist-info → fastapi_rtk-1.0.13.dist-info}/WHEEL +1 -2
  80. fastapi_rtk/backends/gremlinpython/__init__.py +0 -108
  81. fastapi_rtk/backends/gremlinpython/column.py +0 -208
  82. fastapi_rtk/backends/gremlinpython/db.py +0 -228
  83. fastapi_rtk/backends/gremlinpython/exceptions.py +0 -34
  84. fastapi_rtk/backends/gremlinpython/filters.py +0 -461
  85. fastapi_rtk/backends/gremlinpython/interface.py +0 -734
  86. fastapi_rtk/backends/gremlinpython/model.py +0 -364
  87. fastapi_rtk/backends/gremlinpython/session.py +0 -23
  88. fastapi_rtk/cli/commands.py +0 -295
  89. fastapi_rtk-0.2.27.dist-info/METADATA +0 -23
  90. fastapi_rtk-0.2.27.dist-info/RECORD +0 -126
  91. fastapi_rtk-0.2.27.dist-info/top_level.txt +0 -1
  92. /fastapi_rtk/cli/{templates → commands/db/templates}/fastapi/README +0 -0
  93. /fastapi_rtk/cli/{templates → commands/db/templates}/fastapi/alembic.ini.mako +0 -0
  94. /fastapi_rtk/cli/{templates → commands/db/templates}/fastapi/script.py.mako +0 -0
  95. /fastapi_rtk/cli/{templates → commands/db/templates}/fastapi-multidb/README +0 -0
  96. /fastapi_rtk/cli/{templates → commands/db/templates}/fastapi-multidb/alembic.ini.mako +0 -0
  97. {fastapi_rtk-0.2.27.dist-info → fastapi_rtk-1.0.13.dist-info}/entry_points.txt +0 -0
  98. {fastapi_rtk-0.2.27.dist-info → fastapi_rtk-1.0.13.dist-info}/licenses/LICENSE +0 -0
fastapi_rtk/auth/auth.py CHANGED
@@ -142,15 +142,6 @@ class Authenticator(BaseAuthenticator):
142
142
  except HTTPException as e:
143
143
  if not default_to_none:
144
144
  raise e
145
-
146
- # Retrieve list of apis, that user has access to
147
- if user:
148
- user.permissions = []
149
- for role in user.roles:
150
- for permission_api in role.permissions:
151
- user.permissions.append(permission_api.api.name)
152
- user.permissions = list(set(user.permissions))
153
-
154
145
  return user, token
155
146
 
156
147
 
@@ -8,6 +8,10 @@ from .session import *
8
8
 
9
9
  __all__ = [
10
10
  # .column
11
+ "GenericPKAutoIncrement",
12
+ "GenericUnset",
13
+ "PK_AUTO_INCREMENT",
14
+ "UNSET",
11
15
  "GenericColumn",
12
16
  # .db
13
17
  "GenericQueryBuilder",
@@ -36,10 +40,12 @@ __all__ = [
36
40
  "GenericFilterSmallerEqual",
37
41
  "GenericFilterIn",
38
42
  "GenericFilterGlobal",
43
+ "GenericBaseOprFilter",
39
44
  "GenericFilterConverter",
40
45
  # .interface
41
46
  "GenericInterface",
42
47
  # .model
48
+ "GenericMapper",
43
49
  "GenericModel",
44
50
  # .session
45
51
  "GenericSession",
@@ -9,31 +9,38 @@ from pydantic import (
9
9
  create_model,
10
10
  )
11
11
 
12
+ from ...mixins import LoggerMixin
12
13
  from ...utils import T
13
14
  from .exceptions import ColumnInvalidTypeException, GenericColumnException
14
15
 
15
16
  if typing.TYPE_CHECKING:
16
17
  from .model import GenericModel
17
18
 
18
- __all__ = ["GenericColumn"]
19
+ __all__ = [
20
+ "GenericPKAutoIncrement",
21
+ "GenericUnset",
22
+ "PK_AUTO_INCREMENT",
23
+ "UNSET",
24
+ "GenericColumn",
25
+ ]
19
26
 
20
27
 
21
- class _CapitalClass:
28
+ class GenericCapitalClass:
22
29
  def __repr__(self) -> str:
23
- return self.__class__.__name__.lstrip("_").upper()
30
+ return self.__class__.__name__.upper()
24
31
 
25
32
 
26
- class _PKAutoIncrement(_CapitalClass): ...
33
+ class GenericPKAutoIncrement(GenericCapitalClass): ...
27
34
 
28
35
 
29
- class _Unset(_CapitalClass): ...
36
+ class GenericUnset(GenericCapitalClass): ...
30
37
 
31
38
 
32
- PK_AUTO_INCREMENT = _PKAutoIncrement()
33
- UNSET = _Unset()
39
+ PK_AUTO_INCREMENT = GenericPKAutoIncrement()
40
+ UNSET = GenericUnset()
34
41
 
35
42
 
36
- class GenericColumn(typing.Generic[T]):
43
+ class GenericColumn(typing.Generic[T], LoggerMixin):
37
44
  """
38
45
  A generic column for `GenericModel`. This works similar to a SQLAlchemy column.
39
46
 
@@ -51,7 +58,7 @@ class GenericColumn(typing.Generic[T]):
51
58
  auto_increment: bool
52
59
  unique: bool
53
60
  nullable: bool
54
- default: T | typing.Callable[["GenericModel"], T] | _Unset
61
+ default: T | typing.Callable[["GenericModel"], T] | GenericUnset
55
62
 
56
63
  _validation_model: BaseModel = None
57
64
  _col_name: str = None
@@ -67,7 +74,7 @@ class GenericColumn(typing.Generic[T]):
67
74
  default: T
68
75
  | typing.Callable[[], T]
69
76
  | typing.Callable[["GenericModel"], T]
70
- | _Unset = UNSET,
77
+ | GenericUnset = UNSET,
71
78
  ):
72
79
  """
73
80
  Initializes a GenericColumn instance.
@@ -133,7 +140,9 @@ class GenericColumn(typing.Generic[T]):
133
140
  if callable(default_value):
134
141
  default_value = default_value(instance)
135
142
 
136
- value: T | _Unset = instance.__dict__.get(f"_{self._col_name}", default_value)
143
+ value: T | GenericUnset = instance.__dict__.get(
144
+ f"_{self._col_name}", default_value
145
+ )
137
146
 
138
147
  if value is default_value:
139
148
  if self.nullable and value is UNSET:
@@ -143,7 +152,7 @@ class GenericColumn(typing.Generic[T]):
143
152
 
144
153
  return value
145
154
 
146
- def __set__(self, instance: "GenericModel", value: T | _Unset) -> None:
155
+ def __set__(self, instance: "GenericModel", value: T | GenericUnset) -> None:
147
156
  try:
148
157
  validated = self._validation_model.model_validate({self._col_name: value})
149
158
  except ValidationError as e:
@@ -1,9 +1,12 @@
1
1
  import typing
2
2
 
3
- from fastapi import HTTPException
3
+ import fastapi
4
4
 
5
5
  from ...bases.db import AbstractQueryBuilder
6
+ from ...exceptions import HTTPWithValidationException
7
+ from ...lang import translate
6
8
  from ...utils import lazy
9
+ from .filters import GenericBaseOprFilter
7
10
  from .session import GenericSession
8
11
 
9
12
  if typing.TYPE_CHECKING:
@@ -55,18 +58,50 @@ class GenericQueryBuilder(AbstractQueryBuilder[GenericSession]):
55
58
  filter_class = f
56
59
  break
57
60
  if not filter_class:
58
- raise HTTPException(
59
- status_code=400, detail=f"Invalid filter opr: {filter.opr}"
61
+ raise HTTPWithValidationException(
62
+ fastapi.status.HTTP_400_BAD_REQUEST,
63
+ "invalid_key",
64
+ "query",
65
+ "filters",
66
+ translate("Invalid filter operator: {operator}", operator=filter.opr),
67
+ filter.model_dump(mode="json"),
60
68
  )
61
69
 
62
- col = filter.col
63
- value = filter.value
64
-
65
- return self.apply_filter_class(statement, col, filter_class, value)
70
+ return self.apply_filter_class(
71
+ statement, filter.col, filter_class, filter.value
72
+ )
66
73
 
67
74
  def apply_filter_class(self, statement, col, filter_class, value):
68
75
  return filter_class.apply(statement, col, value)
69
76
 
77
+ def apply_opr_filter(self, statement, filter):
78
+ filter_class = None
79
+ for f in self.datamodel.opr_filters:
80
+ if f.arg_name == filter.opr:
81
+ filter_class = f
82
+ break
83
+ if not filter_class:
84
+ raise HTTPWithValidationException(
85
+ fastapi.status.HTTP_400_BAD_REQUEST,
86
+ "invalid_key",
87
+ "query",
88
+ "opr_filters",
89
+ translate(
90
+ "Invalid opr_filter operator: {operator}", operator=filter.opr
91
+ ),
92
+ filter.model_dump(mode="json"),
93
+ )
94
+
95
+ return self.apply_opr_filter_class(statement, filter_class, filter.value)
96
+
97
+ def apply_opr_filter_class(self, statement, filter_class, value):
98
+ params = [statement]
99
+ if isinstance(filter_class, GenericBaseOprFilter):
100
+ params.append(value)
101
+ else:
102
+ params.extend([None, value])
103
+ return filter_class.apply(statement, *params)
104
+
70
105
  def apply_global_filter(self, statement, columns, global_filter):
71
106
  return self.apply_filter_class(
72
107
  statement,
@@ -2,8 +2,9 @@ import enum
2
2
  import typing
3
3
  from datetime import date, datetime
4
4
 
5
- from ...bases.filter import AbstractBaseFilter
5
+ from ...bases.filter import AbstractBaseFilter, AbstractBaseOprFilter
6
6
  from ...const import logger
7
+ from ...lang import lazy_text
7
8
  from .session import GenericSession
8
9
 
9
10
  if typing.TYPE_CHECKING:
@@ -27,6 +28,7 @@ __all__ = [
27
28
  "GenericFilterSmallerEqual",
28
29
  "GenericFilterIn",
29
30
  "GenericFilterGlobal",
31
+ "GenericBaseOprFilter",
30
32
  "GenericFilterConverter",
31
33
  ]
32
34
 
@@ -76,7 +78,7 @@ class GenericBaseFilter(AbstractBaseFilter[GenericSession]):
76
78
 
77
79
 
78
80
  class GenericFilterTextContains(GenericBaseFilter):
79
- name = "Text contains"
81
+ name = lazy_text("Text contains")
80
82
  arg_name = "tc"
81
83
 
82
84
  def apply(self, statement, col, value):
@@ -84,7 +86,7 @@ class GenericFilterTextContains(GenericBaseFilter):
84
86
 
85
87
 
86
88
  class GenericFilterEqual(GenericBaseFilter):
87
- name = "Equal to"
89
+ name = lazy_text("Equal to")
88
90
  arg_name = "eq"
89
91
 
90
92
  def apply(
@@ -112,7 +114,7 @@ class GenericFilterNotEqual(GenericBaseFilter):
112
114
 
113
115
 
114
116
  class GenericFilterStartsWith(GenericBaseFilter):
115
- name = "Starts with"
117
+ name = lazy_text("Starts with")
116
118
  arg_name = "sw"
117
119
 
118
120
  def apply(self, statement, col: str, value):
@@ -121,7 +123,7 @@ class GenericFilterStartsWith(GenericBaseFilter):
121
123
 
122
124
 
123
125
  class GenericFilterNotStartsWith(GenericBaseFilter):
124
- name = "Not Starts with"
126
+ name = lazy_text("Not Starts with")
125
127
  arg_name = "nsw"
126
128
 
127
129
  def apply(self, statement, col: str, value):
@@ -130,7 +132,7 @@ class GenericFilterNotStartsWith(GenericBaseFilter):
130
132
 
131
133
 
132
134
  class GenericFilterEndsWith(GenericBaseFilter):
133
- name = "Ends with"
135
+ name = lazy_text("Ends with")
134
136
  arg_name = "ew"
135
137
 
136
138
  def apply(self, statement, col: str, value):
@@ -139,7 +141,7 @@ class GenericFilterEndsWith(GenericBaseFilter):
139
141
 
140
142
 
141
143
  class GenericFilterNotEndsWith(GenericBaseFilter):
142
- name = "Not Ends with"
144
+ name = lazy_text("Not Ends with")
143
145
  arg_name = "new"
144
146
 
145
147
  def apply(self, statement, col: str, value):
@@ -148,7 +150,7 @@ class GenericFilterNotEndsWith(GenericBaseFilter):
148
150
 
149
151
 
150
152
  class GenericFilterContains(GenericBaseFilter):
151
- name = "Contains"
153
+ name = lazy_text("Contains")
152
154
  arg_name = "ct"
153
155
 
154
156
  def apply(self, statement, col: str, value):
@@ -157,7 +159,7 @@ class GenericFilterContains(GenericBaseFilter):
157
159
 
158
160
 
159
161
  class GenericFilterIContains(GenericBaseFilter):
160
- name = "Contains (insensitive)"
162
+ name = lazy_text("Contains (insensitive)")
161
163
  arg_name = "ict"
162
164
 
163
165
  def apply(self, statement, col: str, value):
@@ -166,7 +168,7 @@ class GenericFilterIContains(GenericBaseFilter):
166
168
 
167
169
 
168
170
  class GenericFilterNotContains(GenericBaseFilter):
169
- name = "Not Contains"
171
+ name = lazy_text("Not Contains")
170
172
  arg_name = "nct"
171
173
 
172
174
  def apply(self, statement, col: str, value):
@@ -175,7 +177,7 @@ class GenericFilterNotContains(GenericBaseFilter):
175
177
 
176
178
 
177
179
  class GenericFilterGreater(GenericBaseFilter):
178
- name = "Greater than"
180
+ name = lazy_text("Greater than")
179
181
  arg_name = "gt"
180
182
 
181
183
  def apply(self, statement, col: str, value: int | date | datetime):
@@ -184,7 +186,7 @@ class GenericFilterGreater(GenericBaseFilter):
184
186
 
185
187
 
186
188
  class GenericFilterSmaller(GenericBaseFilter):
187
- name = "Smaller than"
189
+ name = lazy_text("Smaller than")
188
190
  arg_name = "lt"
189
191
 
190
192
  def apply(self, statement, col: str, value: int | date | datetime):
@@ -193,7 +195,7 @@ class GenericFilterSmaller(GenericBaseFilter):
193
195
 
194
196
 
195
197
  class GenericFilterGreaterEqual(GenericBaseFilter):
196
- name = "Greater equal"
198
+ name = lazy_text("Greater equal")
197
199
  arg_name = "ge"
198
200
 
199
201
  def apply(self, statement, col: str, value: int | date | datetime):
@@ -202,7 +204,7 @@ class GenericFilterGreaterEqual(GenericBaseFilter):
202
204
 
203
205
 
204
206
  class GenericFilterSmallerEqual(GenericBaseFilter):
205
- name = "Smaller equal"
207
+ name = lazy_text("Smaller equal")
206
208
  arg_name = "le"
207
209
 
208
210
  def apply(self, statement, col: str, value: int | date | datetime):
@@ -211,7 +213,7 @@ class GenericFilterSmallerEqual(GenericBaseFilter):
211
213
 
212
214
 
213
215
  class GenericFilterIn(GenericBaseFilter):
214
- name = "One of"
216
+ name = lazy_text("One of")
215
217
  arg_name = "in"
216
218
 
217
219
  def apply(self, statement, col: str, value: list[str | bool | int]):
@@ -220,7 +222,7 @@ class GenericFilterIn(GenericBaseFilter):
220
222
 
221
223
 
222
224
  class GenericFilterGlobal(GenericBaseFilter):
223
- name = "Global Filter"
225
+ name = lazy_text("Global Filter")
224
226
  arg_name = "global"
225
227
 
226
228
  def apply(self, statement, col: str, value: str):
@@ -228,6 +230,9 @@ class GenericFilterGlobal(GenericBaseFilter):
228
230
  return statement.global_filter(col, value)
229
231
 
230
232
 
233
+ class GenericBaseOprFilter(AbstractBaseOprFilter, GenericBaseFilter): ...
234
+
235
+
231
236
  class GenericFilterConverter:
232
237
  """
233
238
  Helper class to get available filters for a generic column type.
@@ -15,11 +15,15 @@ from .session import GenericSession
15
15
 
16
16
  __all__ = ["GenericInterface"]
17
17
 
18
+ GenericModelType = typing.TypeVar("GenericModelType", bound=GenericModel)
19
+
18
20
 
19
21
  class lazy(lazy_self["GenericInterface", T]): ...
20
22
 
21
23
 
22
- class GenericInterface(AbstractInterface[GenericModel, GenericSession, GenericColumn]):
24
+ class GenericInterface(
25
+ AbstractInterface[GenericModelType, GenericSession, GenericColumn]
26
+ ):
23
27
  """
24
28
  Represents an interface for a Generic model (Based on pydantic).
25
29
  """
@@ -33,7 +37,7 @@ class GenericInterface(AbstractInterface[GenericModel, GenericSession, GenericCo
33
37
  query_count = lazy(lambda self: GenericQueryBuilder(self, self.session))
34
38
 
35
39
  def __init__(
36
- self, obj: typing.Type[GenericModel], session: typing.Type[GenericSession]
40
+ self, obj: typing.Type[GenericModelType], session: typing.Type[GenericSession]
37
41
  ):
38
42
  super().__init__(obj)
39
43
  if not isinstance(session, type):
@@ -62,23 +66,23 @@ class GenericInterface(AbstractInterface[GenericModel, GenericSession, GenericCo
62
66
  relevant_params.pop("page_size", None)
63
67
  relevant_params.pop("order_column", None)
64
68
  relevant_params.pop("order_direction", None)
65
-
66
69
  statement = await self.query_count.build_query(relevant_params)
67
70
  return statement.count()
68
71
 
69
- async def get_many(self, session, params=None):
72
+ async def get_many(self, session, params=None) -> list[GenericModelType]:
70
73
  statement = await self.query.build_query(params)
71
74
  return statement.all()
72
75
 
73
- async def get_one(self, session, params=None):
76
+ async def get_one(self, session, params=None) -> GenericModelType | None:
74
77
  statement = await self.query.build_query(params)
75
78
  return statement.first()
76
79
 
77
- async def yield_per(self, session, params=None):
80
+ async def yield_per(
81
+ self, session, params=None
82
+ ) -> typing.AsyncGenerator[list[GenericModelType], None]:
78
83
  relevant_params = params.copy() if params else DBQueryParams()
79
84
  relevant_params.pop("page", None)
80
85
  page_size = relevant_params.pop("page_size", 100)
81
-
82
86
  statement = await self.query.build_query(relevant_params)
83
87
  items = statement.yield_per(page_size)
84
88
  while True:
@@ -98,7 +102,9 @@ class GenericInterface(AbstractInterface[GenericModel, GenericSession, GenericCo
98
102
  session.refresh(item)
99
103
  return item
100
104
 
101
- async def edit(self, session, item, *, flush=True, commit=True, refresh=False):
105
+ async def edit(
106
+ self, session, item, *, flush=True, commit=True, refresh=False
107
+ ) -> GenericModelType:
102
108
  result = session.merge(item)
103
109
  if flush:
104
110
  session.flush()
@@ -2,7 +2,7 @@ import typing
2
2
 
3
3
  from ...bases.model import BasicModel
4
4
  from ...utils import T, lazy
5
- from .column import UNSET, GenericColumn, _PKAutoIncrement
5
+ from .column import UNSET, GenericColumn, GenericPKAutoIncrement
6
6
  from .exceptions import (
7
7
  ColumnNotSetException,
8
8
  MultipleColumnsException,
@@ -13,10 +13,10 @@ from .exceptions import (
13
13
  if typing.TYPE_CHECKING:
14
14
  from .session import Table
15
15
 
16
- __all__ = ["GenericModel"]
16
+ __all__ = ["GenericMapper", "GenericModel"]
17
17
 
18
18
 
19
- class Mapper(typing.Generic[T]):
19
+ class GenericMapper(typing.Generic[T]):
20
20
  """
21
21
  Mapper class that contains a model's definitions.
22
22
  """
@@ -46,8 +46,8 @@ class GenericModel(BasicModel):
46
46
  GenericModel is a base class for all models that does not use SQLAlchemy, but want to behave like one.
47
47
  """
48
48
 
49
- __mapper__: Mapper[GenericColumn] = lazy(
50
- lambda: Mapper[GenericColumn](), only_instance=False
49
+ __mapper__: GenericMapper[GenericColumn] = lazy(
50
+ lambda: GenericMapper[GenericColumn](), only_instance=False
51
51
  )
52
52
  """
53
53
  A mapper that contains the primary key, properties, and columns of the model.
@@ -92,21 +92,28 @@ class GenericModel(BasicModel):
92
92
  if not cls.__mapper__.pk:
93
93
  raise PKMissingException(cls.__name__, "Primary key is missing")
94
94
 
95
- def __init__(self, **kwargs):
95
+ def __init__(self, *, __check_on_init__=True, **kwargs):
96
+ """
97
+ Initialize the model with the given keyword arguments.
98
+
99
+ Args:
100
+ __check_on_init__ (bool, optional): Whether to check the model's validity on initialization. Defaults to True.
101
+ """
96
102
  if not kwargs:
97
103
  return
98
104
  for key, value in kwargs.items():
99
105
  if key not in self.__mapper__.properties.keys():
100
106
  continue
101
107
  setattr(self, key, value)
102
- self.is_model_valid()
108
+ if __check_on_init__:
109
+ self.is_model_valid()
103
110
 
104
- def get_pk(self) -> str | int | _PKAutoIncrement:
111
+ def get_pk(self) -> str | int | GenericPKAutoIncrement:
105
112
  """
106
113
  Get the primary key of the model.
107
114
 
108
115
  Returns:
109
- str | int | _PKAutoIncrement: The primary key of the model.
116
+ str | int | GenericPKAutoIncrement: The primary key of the model.
110
117
  """
111
118
  return getattr(self, self.__mapper__.pk)
112
119
 
@@ -122,11 +129,12 @@ class GenericModel(BasicModel):
122
129
  def get_col_type(self, col_name: str):
123
130
  return self.__mapper__.properties[col_name].col_type
124
131
 
125
- def update(self, data):
132
+ def update(self, data, *, check=True):
126
133
  super().update(data)
127
134
 
128
135
  # Check if the model is valid
129
- self.is_model_valid()
136
+ if check:
137
+ self.is_model_valid()
130
138
 
131
139
  def is_model_valid(self):
132
140
  """
@@ -60,6 +60,7 @@ __all__ = [
60
60
  "FilterRelationOneToManyOrManyToManyIn",
61
61
  "FilterRelationOneToManyOrManyToManyNotIn",
62
62
  "FilterGlobal",
63
+ "BaseOprFilter",
63
64
  "SQLAFilterConverter",
64
65
  # .interface
65
66
  "SQLAInterface",