database-wrapper 0.1.86__py3-none-any.whl → 0.2.2__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.
- database_wrapper/config.py +3 -3
- database_wrapper/db_backend.py +41 -40
- database_wrapper/db_data_model.py +102 -102
- database_wrapper/db_wrapper.py +179 -159
- database_wrapper/db_wrapper_async.py +177 -165
- database_wrapper/db_wrapper_mixin.py +72 -72
- database_wrapper/serialization.py +15 -17
- database_wrapper/utils/dataclass_addons.py +5 -5
- {database_wrapper-0.1.86.dist-info → database_wrapper-0.2.2.dist-info}/METADATA +5 -5
- database_wrapper-0.2.2.dist-info/RECORD +17 -0
- {database_wrapper-0.1.86.dist-info → database_wrapper-0.2.2.dist-info}/WHEEL +1 -1
- database_wrapper-0.1.86.dist-info/RECORD +0 -17
- {database_wrapper-0.1.86.dist-info → database_wrapper-0.2.2.dist-info}/top_level.txt +0 -0
database_wrapper/config.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import Any
|
|
|
3
3
|
CONFIG: dict[str, Any] = {
|
|
4
4
|
# These are supposed to be set automatically by a git pre-compile script
|
|
5
5
|
# They are one git commit hash behind, if used automatically
|
|
6
|
-
"git_commit_hash": "
|
|
7
|
-
"git_commit_date": "
|
|
8
|
-
"app_version": "0.
|
|
6
|
+
"git_commit_hash": "c2bc2271cb75bcb46a0114013afa9b34dcb71aa6",
|
|
7
|
+
"git_commit_date": "16.05.2025 21:56",
|
|
8
|
+
"app_version": "0.2.2",
|
|
9
9
|
}
|
database_wrapper/db_backend.py
CHANGED
|
@@ -10,20 +10,20 @@ class DatabaseBackend:
|
|
|
10
10
|
config: Any
|
|
11
11
|
""" Database configuration """
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
connection_timeout: int
|
|
14
14
|
""" Connection timeout """
|
|
15
15
|
|
|
16
16
|
name: str
|
|
17
17
|
""" Instance name """
|
|
18
18
|
|
|
19
19
|
# TODO: This should be made to increase exponentially
|
|
20
|
-
|
|
20
|
+
slow_down_timeout: int
|
|
21
21
|
""" How long to wait before trying to reconnect """
|
|
22
22
|
|
|
23
23
|
pool: Any
|
|
24
24
|
""" Connection pool """
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
pool_async: Any
|
|
27
27
|
""" Async connection pool """
|
|
28
28
|
|
|
29
29
|
connection: Any
|
|
@@ -32,19 +32,19 @@ class DatabaseBackend:
|
|
|
32
32
|
cursor: Any
|
|
33
33
|
""" Cursor to database """
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
context_connection: ContextVar[Any | None]
|
|
36
36
|
""" Connection used in context manager """
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
context_connection_async: ContextVar[Any | None]
|
|
39
39
|
""" Connection used in async context manager """
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
logger_name: str
|
|
42
42
|
""" Logger name """
|
|
43
43
|
|
|
44
44
|
logger: logging.Logger
|
|
45
45
|
""" Logger """
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
shutdown_requested: Event
|
|
48
48
|
"""
|
|
49
49
|
Event to signal shutdown
|
|
50
50
|
Used to stop database pool from creating new connections
|
|
@@ -56,16 +56,16 @@ class DatabaseBackend:
|
|
|
56
56
|
|
|
57
57
|
def __init__(
|
|
58
58
|
self,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
db_config: Any,
|
|
60
|
+
connection_timeout: int = 5,
|
|
61
|
+
instance_name: str = "database_backend",
|
|
62
|
+
slow_down_timeout: int = 5,
|
|
63
63
|
) -> None:
|
|
64
64
|
"""
|
|
65
65
|
Main concept here is that in init we do not connect to database,
|
|
66
66
|
so that class instances can be safely made regardless of connection statuss.
|
|
67
67
|
|
|
68
|
-
Remember to call open() or
|
|
68
|
+
Remember to call open() or open_pool() before using this class.
|
|
69
69
|
Close will be called automatically when class is destroyed.
|
|
70
70
|
|
|
71
71
|
Contexts are not implemented here, but in child classes should be used
|
|
@@ -75,23 +75,24 @@ class DatabaseBackend:
|
|
|
75
75
|
if not upon destroying the class, an error will be raised that method was not awaited.
|
|
76
76
|
"""
|
|
77
77
|
|
|
78
|
-
self.config =
|
|
79
|
-
self.
|
|
80
|
-
self.name =
|
|
81
|
-
self.
|
|
78
|
+
self.config = db_config
|
|
79
|
+
self.connection_timeout = connection_timeout
|
|
80
|
+
self.name = instance_name
|
|
81
|
+
self.slow_down_timeout = slow_down_timeout
|
|
82
82
|
|
|
83
|
-
self.
|
|
84
|
-
self.logger = logging.getLogger(self.
|
|
83
|
+
self.logger_name = f"{__name__}.{self.__class__.__name__}.{self.name}"
|
|
84
|
+
self.logger = logging.getLogger(self.logger_name)
|
|
85
85
|
|
|
86
86
|
self.pool = None
|
|
87
|
-
self.
|
|
87
|
+
self.pool_async = None
|
|
88
88
|
|
|
89
89
|
self.connection = None
|
|
90
90
|
self.cursor = None
|
|
91
|
-
self.
|
|
92
|
-
self.
|
|
93
|
-
self.
|
|
94
|
-
f"db_connection_{self.name}_async",
|
|
91
|
+
self.shutdown_requested = Event()
|
|
92
|
+
self.context_connection = ContextVar(f"db_connection_{self.name}", default=None)
|
|
93
|
+
self.context_connection_async = ContextVar(
|
|
94
|
+
f"db_connection_{self.name}_async",
|
|
95
|
+
default=None,
|
|
95
96
|
)
|
|
96
97
|
|
|
97
98
|
def __del__(self) -> None:
|
|
@@ -100,14 +101,14 @@ class DatabaseBackend:
|
|
|
100
101
|
|
|
101
102
|
# Clean up connections
|
|
102
103
|
self.close()
|
|
103
|
-
self.
|
|
104
|
+
self.close_pool()
|
|
104
105
|
|
|
105
106
|
# Clean just in case
|
|
106
107
|
del self.connection
|
|
107
108
|
del self.cursor
|
|
108
109
|
|
|
109
110
|
del self.pool
|
|
110
|
-
del self.
|
|
111
|
+
del self.pool_async
|
|
111
112
|
|
|
112
113
|
###############
|
|
113
114
|
### Context ###
|
|
@@ -133,11 +134,11 @@ class DatabaseBackend:
|
|
|
133
134
|
### Connection ###
|
|
134
135
|
##################
|
|
135
136
|
|
|
136
|
-
def
|
|
137
|
+
def open_pool(self) -> Any:
|
|
137
138
|
"""Open connection pool"""
|
|
138
139
|
...
|
|
139
140
|
|
|
140
|
-
def
|
|
141
|
+
def close_pool(self) -> Any:
|
|
141
142
|
"""Close connection pool"""
|
|
142
143
|
...
|
|
143
144
|
|
|
@@ -157,7 +158,7 @@ class DatabaseBackend:
|
|
|
157
158
|
self.connection.close()
|
|
158
159
|
self.connection = None
|
|
159
160
|
|
|
160
|
-
def
|
|
161
|
+
def new_connection(self) -> Any:
|
|
161
162
|
"""
|
|
162
163
|
Create new connection
|
|
163
164
|
|
|
@@ -168,7 +169,7 @@ class DatabaseBackend:
|
|
|
168
169
|
"""
|
|
169
170
|
raise Exception("Not implemented")
|
|
170
171
|
|
|
171
|
-
def
|
|
172
|
+
def return_connection(self, connection: Any) -> Any:
|
|
172
173
|
"""
|
|
173
174
|
Return connection to pool
|
|
174
175
|
|
|
@@ -190,7 +191,7 @@ class DatabaseBackend:
|
|
|
190
191
|
"""
|
|
191
192
|
raise Exception("Not implemented")
|
|
192
193
|
|
|
193
|
-
def
|
|
194
|
+
def has_connection(self) -> bool:
|
|
194
195
|
"""
|
|
195
196
|
Check if connection is alive/set.
|
|
196
197
|
|
|
@@ -199,7 +200,7 @@ class DatabaseBackend:
|
|
|
199
200
|
"""
|
|
200
201
|
return self.connection is not None
|
|
201
202
|
|
|
202
|
-
def
|
|
203
|
+
def has_cursor(self) -> bool:
|
|
203
204
|
"""
|
|
204
205
|
Check if cursor is alive/set.
|
|
205
206
|
|
|
@@ -212,14 +213,14 @@ class DatabaseBackend:
|
|
|
212
213
|
### Helpers ###
|
|
213
214
|
###############
|
|
214
215
|
|
|
215
|
-
def
|
|
216
|
+
def fix_socket_timeouts(self, fd: Any) -> None:
|
|
216
217
|
# Lets do some socket magic
|
|
217
218
|
s = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
|
|
218
219
|
# Enable sending of keep-alive messages
|
|
219
220
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
220
221
|
# Time the connection needs to remain idle before start sending
|
|
221
222
|
# keepalive probes
|
|
222
|
-
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, self.
|
|
223
|
+
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, self.connection_timeout)
|
|
223
224
|
# Time between individual keepalive probes
|
|
224
225
|
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1)
|
|
225
226
|
# The maximum number of keepalive probes should send before dropping
|
|
@@ -228,27 +229,27 @@ class DatabaseBackend:
|
|
|
228
229
|
# To set timeout for an RTO you must set TCP_USER_TIMEOUT timeout
|
|
229
230
|
# (in milliseconds) for socket.
|
|
230
231
|
s.setsockopt(
|
|
231
|
-
socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, self.
|
|
232
|
+
socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, self.connection_timeout * 1000
|
|
232
233
|
)
|
|
233
234
|
|
|
234
235
|
####################
|
|
235
236
|
### Transactions ###
|
|
236
237
|
####################
|
|
237
238
|
|
|
238
|
-
def
|
|
239
|
+
def begin_transaction(self) -> Any:
|
|
239
240
|
"""Start transaction"""
|
|
240
241
|
raise Exception("Not implemented")
|
|
241
242
|
|
|
242
|
-
def
|
|
243
|
+
def commit_transaction(self) -> Any:
|
|
243
244
|
"""Commit transaction"""
|
|
244
245
|
raise Exception("Not implemented")
|
|
245
246
|
|
|
246
|
-
def
|
|
247
|
+
def rollback_transaction(self) -> Any:
|
|
247
248
|
"""Rollback transaction"""
|
|
248
249
|
raise Exception("Not implemented")
|
|
249
250
|
|
|
250
251
|
# @contextmanager
|
|
251
|
-
def transaction(self,
|
|
252
|
+
def transaction(self, db_conn: Any = None) -> Any:
|
|
252
253
|
"""
|
|
253
254
|
Transaction context manager
|
|
254
255
|
|
|
@@ -261,11 +262,11 @@ class DatabaseBackend:
|
|
|
261
262
|
### Data ###
|
|
262
263
|
############
|
|
263
264
|
|
|
264
|
-
def
|
|
265
|
+
def last_insert_id(self) -> int:
|
|
265
266
|
"""Get last inserted row id generated by auto increment"""
|
|
266
267
|
raise Exception("Not implemented")
|
|
267
268
|
|
|
268
|
-
def
|
|
269
|
+
def affected_rows(self) -> int:
|
|
269
270
|
"""Get affected rows count"""
|
|
270
271
|
raise Exception("Not implemented")
|
|
271
272
|
|
|
@@ -9,9 +9,9 @@ from typing import Any, Callable, Literal, NotRequired, Type, TypeVar, TypedDict
|
|
|
9
9
|
|
|
10
10
|
from .serialization import (
|
|
11
11
|
SerializeType,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
deserialize_value,
|
|
13
|
+
json_encoder,
|
|
14
|
+
serialize_value,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
EnumType = TypeVar("EnumType", bound=Enum)
|
|
@@ -33,34 +33,34 @@ class DBDataModel:
|
|
|
33
33
|
Base class for all database models.
|
|
34
34
|
|
|
35
35
|
Attributes:
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
36
|
+
- schema_name (str): The name of the schema in the database.
|
|
37
|
+
- table_name (str): The name of the table in the database.
|
|
38
|
+
- table_alias (str): The alias of the table in the database.
|
|
39
|
+
- id_key (str): The name of the primary key column in the database.
|
|
40
|
+
- id_value (Any): The value of the primary key for the current instance.
|
|
41
41
|
- id (int): The primary key value for the current instance.
|
|
42
42
|
|
|
43
43
|
Methods:
|
|
44
44
|
- __post_init__(): Initializes the instance after it has been created.
|
|
45
45
|
- __repr__(): Returns a string representation of the instance.
|
|
46
46
|
- __str__(): Returns a JSON string representation of the instance.
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
47
|
+
- to_dict(): Returns a dictionary representation of the instance.
|
|
48
|
+
- to_formatted_dict(): Returns a formatted dictionary representation of the instance.
|
|
49
|
+
- to_json_schema(): Returns a JSON schema for the instance.
|
|
50
|
+
- json_encoder(obj: Any): Encodes the given object as JSON.
|
|
51
|
+
- to_json_string(pretty: bool = False): Returns a JSON string representation of the instance.
|
|
52
|
+
- str_to_datetime(value: Any): Converts a string to a datetime object.
|
|
53
|
+
- str_to_bool(value: Any): Converts a string to a boolean value.
|
|
54
|
+
- str_to_int(value: Any): Converts a string to an integer value.
|
|
55
55
|
- validate(): Validates the instance.
|
|
56
56
|
|
|
57
57
|
To enable storing and updating fields that by default are not stored or updated, use the following methods:
|
|
58
|
-
-
|
|
59
|
-
-
|
|
58
|
+
- set_store(field_name: str, enable: bool = True): Enable/Disable storing a field.
|
|
59
|
+
- set_update(field_name: str, enable: bool = True): Enable/Disable updating a field.
|
|
60
60
|
|
|
61
61
|
To exclude a field from the dictionary representation of the instance, set metadata key "exclude" to True.
|
|
62
62
|
To change exclude status of a field, use the following method:
|
|
63
|
-
-
|
|
63
|
+
- set_exclude(field_name: str, enable: bool = True): Exclude a field from dict representation.
|
|
64
64
|
"""
|
|
65
65
|
|
|
66
66
|
######################
|
|
@@ -68,24 +68,24 @@ class DBDataModel:
|
|
|
68
68
|
######################
|
|
69
69
|
|
|
70
70
|
@property
|
|
71
|
-
def
|
|
71
|
+
def schema_name(self) -> str | None:
|
|
72
72
|
return None
|
|
73
73
|
|
|
74
74
|
@property
|
|
75
|
-
def
|
|
76
|
-
raise NotImplementedError("`
|
|
75
|
+
def table_name(self) -> str:
|
|
76
|
+
raise NotImplementedError("`table_name` property is not implemented")
|
|
77
77
|
|
|
78
78
|
@property
|
|
79
|
-
def
|
|
79
|
+
def table_alias(self) -> str | None:
|
|
80
80
|
return None
|
|
81
81
|
|
|
82
82
|
@property
|
|
83
|
-
def
|
|
83
|
+
def id_key(self) -> str:
|
|
84
84
|
return "id"
|
|
85
85
|
|
|
86
86
|
@property
|
|
87
|
-
def
|
|
88
|
-
return getattr(self, self.
|
|
87
|
+
def id_value(self) -> Any:
|
|
88
|
+
return getattr(self, self.id_key)
|
|
89
89
|
|
|
90
90
|
# Id should be readonly by default and should be always present if record exists
|
|
91
91
|
id: int = field(
|
|
@@ -114,19 +114,19 @@ class DBDataModel:
|
|
|
114
114
|
### Conversion methods ###
|
|
115
115
|
##########################
|
|
116
116
|
|
|
117
|
-
def
|
|
118
|
-
|
|
117
|
+
def fill_data_from_dict(self, kwargs: dict[str, Any]) -> None:
|
|
118
|
+
field_names = set([f.name for f in dataclasses.fields(self)])
|
|
119
119
|
for key in kwargs:
|
|
120
|
-
if key in
|
|
120
|
+
if key in field_names:
|
|
121
121
|
setattr(self, key, kwargs[key])
|
|
122
122
|
|
|
123
123
|
self.__post_init__()
|
|
124
124
|
|
|
125
125
|
# Init data
|
|
126
126
|
def __post_init__(self) -> None:
|
|
127
|
-
for
|
|
128
|
-
metadata = cast(MetadataDict,
|
|
129
|
-
value = getattr(self,
|
|
127
|
+
for field_name, field_obj in self.__dataclass_fields__.items():
|
|
128
|
+
metadata = cast(MetadataDict, field_obj.metadata)
|
|
129
|
+
value = getattr(self, field_name)
|
|
130
130
|
|
|
131
131
|
# If value is not set, we skip it
|
|
132
132
|
if value is None:
|
|
@@ -136,84 +136,84 @@ class DBDataModel:
|
|
|
136
136
|
# we use our serialization function
|
|
137
137
|
# Here we actually need to deserialize the value to correct class type
|
|
138
138
|
serialize = metadata.get("serialize", None)
|
|
139
|
-
|
|
139
|
+
enum_class = metadata.get("enum_class", None)
|
|
140
140
|
if serialize is not None and isinstance(serialize, SerializeType):
|
|
141
|
-
value =
|
|
142
|
-
setattr(self,
|
|
141
|
+
value = deserialize_value(value, serialize, enum_class)
|
|
142
|
+
setattr(self, field_name, value)
|
|
143
143
|
|
|
144
144
|
else:
|
|
145
145
|
deserialize = metadata.get("deserialize", None)
|
|
146
146
|
if deserialize is not None:
|
|
147
147
|
value = deserialize(value)
|
|
148
|
-
setattr(self,
|
|
148
|
+
setattr(self, field_name, value)
|
|
149
149
|
|
|
150
150
|
# String - representation
|
|
151
151
|
def __repr__(self) -> str:
|
|
152
152
|
return "<%s %s>" % (self.__class__.__name__, self.__dict__)
|
|
153
153
|
|
|
154
154
|
def __str__(self) -> str:
|
|
155
|
-
return self.
|
|
155
|
+
return self.to_json_string()
|
|
156
156
|
|
|
157
157
|
# Dict
|
|
158
|
-
def
|
|
159
|
-
|
|
158
|
+
def dict_filter(self, pairs: list[tuple[str, Any]]) -> dict[str, Any]:
|
|
159
|
+
new_dict: dict[str, Any] = {}
|
|
160
160
|
for field in pairs:
|
|
161
|
-
|
|
162
|
-
if
|
|
163
|
-
metadata = cast(MetadataDict,
|
|
161
|
+
class_field = self.__dataclass_fields__.get(field[0], None)
|
|
162
|
+
if class_field is not None:
|
|
163
|
+
metadata = cast(MetadataDict, class_field.metadata)
|
|
164
164
|
if not "exclude" in metadata or not metadata["exclude"]:
|
|
165
|
-
|
|
165
|
+
new_dict[field[0]] = field[1]
|
|
166
166
|
|
|
167
|
-
return
|
|
167
|
+
return new_dict
|
|
168
168
|
|
|
169
|
-
def
|
|
170
|
-
return asdict(self, dict_factory=self.
|
|
169
|
+
def to_dict(self) -> dict[str, Any]:
|
|
170
|
+
return asdict(self, dict_factory=self.dict_filter)
|
|
171
171
|
|
|
172
|
-
def
|
|
173
|
-
return self.
|
|
172
|
+
def to_formatted_dict(self) -> dict[str, Any]:
|
|
173
|
+
return self.to_dict()
|
|
174
174
|
|
|
175
175
|
# JSON
|
|
176
|
-
def
|
|
176
|
+
def to_json_schema(self) -> dict[str, Any]:
|
|
177
177
|
schema: dict[str, Any] = {
|
|
178
178
|
"type": "object",
|
|
179
179
|
"properties": {
|
|
180
180
|
"id": {"type": "number"},
|
|
181
181
|
},
|
|
182
182
|
}
|
|
183
|
-
for
|
|
184
|
-
metadata = cast(MetadataDict,
|
|
183
|
+
for field_name, field_obj in self.__dataclass_fields__.items():
|
|
184
|
+
metadata = cast(MetadataDict, field_obj.metadata)
|
|
185
185
|
assert (
|
|
186
186
|
"db_field" in metadata
|
|
187
187
|
and isinstance(metadata["db_field"], tuple)
|
|
188
188
|
and len(metadata["db_field"]) == 2
|
|
189
|
-
), f"db_field metadata is not set for {
|
|
190
|
-
|
|
191
|
-
schema["properties"][
|
|
189
|
+
), f"db_field metadata is not set for {field_name}"
|
|
190
|
+
field_type: str = metadata["db_field"][1]
|
|
191
|
+
schema["properties"][field_name] = {"type": field_type}
|
|
192
192
|
|
|
193
193
|
return schema
|
|
194
194
|
|
|
195
|
-
def
|
|
196
|
-
return
|
|
195
|
+
def json_encoder(self, obj: Any) -> Any:
|
|
196
|
+
return json_encoder(obj)
|
|
197
197
|
|
|
198
|
-
def
|
|
198
|
+
def to_json_string(self, pretty: bool = False) -> str:
|
|
199
199
|
if pretty:
|
|
200
200
|
return json.dumps(
|
|
201
|
-
self.
|
|
201
|
+
self.to_dict(),
|
|
202
202
|
ensure_ascii=False,
|
|
203
203
|
sort_keys=True,
|
|
204
204
|
indent=4,
|
|
205
205
|
separators=(",", ": "),
|
|
206
|
-
default=self.
|
|
206
|
+
default=self.json_encoder,
|
|
207
207
|
)
|
|
208
208
|
|
|
209
|
-
return json.dumps(self.
|
|
209
|
+
return json.dumps(self.to_dict(), default=self.json_encoder)
|
|
210
210
|
|
|
211
211
|
#######################
|
|
212
212
|
### Helper methods ####
|
|
213
213
|
#######################
|
|
214
214
|
|
|
215
215
|
@staticmethod
|
|
216
|
-
def
|
|
216
|
+
def str_to_datetime(value: Any) -> datetime.datetime:
|
|
217
217
|
if isinstance(value, datetime.datetime):
|
|
218
218
|
return value
|
|
219
219
|
|
|
@@ -227,7 +227,7 @@ class DBDataModel:
|
|
|
227
227
|
return datetime.datetime.now(datetime.UTC)
|
|
228
228
|
|
|
229
229
|
@staticmethod
|
|
230
|
-
def
|
|
230
|
+
def str_to_bool(value: Any) -> bool:
|
|
231
231
|
if isinstance(value, bool):
|
|
232
232
|
return value
|
|
233
233
|
|
|
@@ -241,7 +241,7 @@ class DBDataModel:
|
|
|
241
241
|
return False
|
|
242
242
|
|
|
243
243
|
@staticmethod
|
|
244
|
-
def
|
|
244
|
+
def str_to_int(value: Any) -> int:
|
|
245
245
|
if isinstance(value, int):
|
|
246
246
|
return value
|
|
247
247
|
|
|
@@ -256,61 +256,61 @@ class DBDataModel:
|
|
|
256
256
|
"""
|
|
257
257
|
raise NotImplementedError("`validate` is not implemented")
|
|
258
258
|
|
|
259
|
-
def
|
|
259
|
+
def set_store(self, field_name: str, enable: bool = True) -> None:
|
|
260
260
|
"""
|
|
261
261
|
Enable/Disable storing a field (insert into database)
|
|
262
262
|
"""
|
|
263
|
-
if
|
|
264
|
-
|
|
263
|
+
if field_name in self.__dataclass_fields__:
|
|
264
|
+
current_metadata = cast(
|
|
265
265
|
MetadataDict,
|
|
266
|
-
dict(self.__dataclass_fields__[
|
|
266
|
+
dict(self.__dataclass_fields__[field_name].metadata),
|
|
267
267
|
)
|
|
268
|
-
|
|
269
|
-
self.__dataclass_fields__[
|
|
268
|
+
current_metadata["store"] = enable
|
|
269
|
+
self.__dataclass_fields__[field_name].metadata = current_metadata
|
|
270
270
|
|
|
271
|
-
def
|
|
271
|
+
def set_update(self, field_name: str, enable: bool = True) -> None:
|
|
272
272
|
"""
|
|
273
273
|
Enable/Disable updating a field (update in database)
|
|
274
274
|
"""
|
|
275
|
-
if
|
|
276
|
-
|
|
275
|
+
if field_name in self.__dataclass_fields__:
|
|
276
|
+
current_metadata = cast(
|
|
277
277
|
MetadataDict,
|
|
278
|
-
dict(self.__dataclass_fields__[
|
|
278
|
+
dict(self.__dataclass_fields__[field_name].metadata),
|
|
279
279
|
)
|
|
280
|
-
|
|
281
|
-
self.__dataclass_fields__[
|
|
280
|
+
current_metadata["update"] = enable
|
|
281
|
+
self.__dataclass_fields__[field_name].metadata = current_metadata
|
|
282
282
|
|
|
283
|
-
def
|
|
283
|
+
def set_exclude(self, field_name: str, enable: bool = True) -> None:
|
|
284
284
|
"""
|
|
285
285
|
Exclude a field from dict representation
|
|
286
286
|
"""
|
|
287
|
-
if
|
|
288
|
-
|
|
287
|
+
if field_name in self.__dataclass_fields__:
|
|
288
|
+
current_metadata = cast(
|
|
289
289
|
MetadataDict,
|
|
290
|
-
dict(self.__dataclass_fields__[
|
|
290
|
+
dict(self.__dataclass_fields__[field_name].metadata),
|
|
291
291
|
)
|
|
292
|
-
|
|
293
|
-
self.__dataclass_fields__[
|
|
292
|
+
current_metadata["exclude"] = enable
|
|
293
|
+
self.__dataclass_fields__[field_name].metadata = current_metadata
|
|
294
294
|
|
|
295
295
|
########################
|
|
296
296
|
### Database methods ###
|
|
297
297
|
########################
|
|
298
298
|
|
|
299
|
-
def
|
|
299
|
+
def query_base(self) -> Any:
|
|
300
300
|
"""
|
|
301
301
|
Base query for all queries
|
|
302
302
|
"""
|
|
303
303
|
return None
|
|
304
304
|
|
|
305
|
-
def
|
|
305
|
+
def store_data(self) -> dict[str, Any] | None:
|
|
306
306
|
"""
|
|
307
307
|
Store data to database
|
|
308
308
|
"""
|
|
309
|
-
|
|
310
|
-
for
|
|
311
|
-
metadata = cast(MetadataDict,
|
|
309
|
+
store_data: dict[str, Any] = {}
|
|
310
|
+
for field_name, field_obj in self.__dataclass_fields__.items():
|
|
311
|
+
metadata = cast(MetadataDict, field_obj.metadata)
|
|
312
312
|
if "store" in metadata and metadata["store"] == True:
|
|
313
|
-
|
|
313
|
+
store_data[field_name] = getattr(self, field_name)
|
|
314
314
|
|
|
315
315
|
# If serialize is set, and serialize is a SerializeType,
|
|
316
316
|
# we use our serialization function.
|
|
@@ -319,24 +319,24 @@ class DBDataModel:
|
|
|
319
319
|
serialize = metadata.get("serialize", None)
|
|
320
320
|
if serialize is not None:
|
|
321
321
|
if isinstance(serialize, SerializeType):
|
|
322
|
-
|
|
323
|
-
|
|
322
|
+
store_data[field_name] = serialize_value(
|
|
323
|
+
store_data[field_name], serialize
|
|
324
324
|
)
|
|
325
325
|
else:
|
|
326
|
-
|
|
326
|
+
store_data[field_name] = serialize(store_data[field_name])
|
|
327
327
|
|
|
328
|
-
return
|
|
328
|
+
return store_data
|
|
329
329
|
|
|
330
|
-
def
|
|
330
|
+
def update_data(self) -> dict[str, Any] | None:
|
|
331
331
|
"""
|
|
332
332
|
Update data to database
|
|
333
333
|
"""
|
|
334
334
|
|
|
335
|
-
|
|
336
|
-
for
|
|
337
|
-
metadata = cast(MetadataDict,
|
|
335
|
+
update_data: dict[str, Any] = {}
|
|
336
|
+
for field_name, field_obj in self.__dataclass_fields__.items():
|
|
337
|
+
metadata = cast(MetadataDict, field_obj.metadata)
|
|
338
338
|
if "update" in metadata and metadata["update"] == True:
|
|
339
|
-
|
|
339
|
+
update_data[field_name] = getattr(self, field_name)
|
|
340
340
|
|
|
341
341
|
# If serialize is set, and serialize is a SerializeType,
|
|
342
342
|
# we use our serialization function.
|
|
@@ -345,13 +345,13 @@ class DBDataModel:
|
|
|
345
345
|
serialize = metadata.get("serialize", None)
|
|
346
346
|
if serialize is not None:
|
|
347
347
|
if isinstance(serialize, SerializeType):
|
|
348
|
-
|
|
349
|
-
|
|
348
|
+
update_data[field_name] = serialize_value(
|
|
349
|
+
update_data[field_name], serialize
|
|
350
350
|
)
|
|
351
351
|
else:
|
|
352
|
-
|
|
352
|
+
update_data[field_name] = serialize(update_data[field_name])
|
|
353
353
|
|
|
354
|
-
return
|
|
354
|
+
return update_data
|
|
355
355
|
|
|
356
356
|
|
|
357
357
|
@dataclass
|
|
@@ -409,7 +409,7 @@ class DBDefaultsDataModel(DBDataModel):
|
|
|
409
409
|
},
|
|
410
410
|
)
|
|
411
411
|
|
|
412
|
-
def
|
|
412
|
+
def update_data(self) -> dict[str, Any] | None:
|
|
413
413
|
"""
|
|
414
414
|
Update data to database
|
|
415
415
|
"""
|
|
@@ -417,4 +417,4 @@ class DBDefaultsDataModel(DBDataModel):
|
|
|
417
417
|
# Update updated_at
|
|
418
418
|
self.updated_at = datetime.datetime.now(datetime.UTC)
|
|
419
419
|
|
|
420
|
-
return super().
|
|
420
|
+
return super().update_data()
|