database-wrapper 0.1.85__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 +57 -38
- 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.85.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.85.dist-info → database_wrapper-0.2.2.dist-info}/WHEEL +1 -1
- database_wrapper-0.1.85.dist-info/RECORD +0 -17
- {database_wrapper-0.1.85.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,18 +191,36 @@ class DatabaseBackend:
|
|
|
190
191
|
"""
|
|
191
192
|
raise Exception("Not implemented")
|
|
192
193
|
|
|
194
|
+
def has_connection(self) -> bool:
|
|
195
|
+
"""
|
|
196
|
+
Check if connection is alive/set.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
bool: Connection status
|
|
200
|
+
"""
|
|
201
|
+
return self.connection is not None
|
|
202
|
+
|
|
203
|
+
def has_cursor(self) -> bool:
|
|
204
|
+
"""
|
|
205
|
+
Check if cursor is alive/set.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
bool: Cursor status
|
|
209
|
+
"""
|
|
210
|
+
return self.cursor is not None
|
|
211
|
+
|
|
193
212
|
###############
|
|
194
213
|
### Helpers ###
|
|
195
214
|
###############
|
|
196
215
|
|
|
197
|
-
def
|
|
216
|
+
def fix_socket_timeouts(self, fd: Any) -> None:
|
|
198
217
|
# Lets do some socket magic
|
|
199
218
|
s = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
|
|
200
219
|
# Enable sending of keep-alive messages
|
|
201
220
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
202
221
|
# Time the connection needs to remain idle before start sending
|
|
203
222
|
# keepalive probes
|
|
204
|
-
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, self.
|
|
223
|
+
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, self.connection_timeout)
|
|
205
224
|
# Time between individual keepalive probes
|
|
206
225
|
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1)
|
|
207
226
|
# The maximum number of keepalive probes should send before dropping
|
|
@@ -210,27 +229,27 @@ class DatabaseBackend:
|
|
|
210
229
|
# To set timeout for an RTO you must set TCP_USER_TIMEOUT timeout
|
|
211
230
|
# (in milliseconds) for socket.
|
|
212
231
|
s.setsockopt(
|
|
213
|
-
socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, self.
|
|
232
|
+
socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, self.connection_timeout * 1000
|
|
214
233
|
)
|
|
215
234
|
|
|
216
235
|
####################
|
|
217
236
|
### Transactions ###
|
|
218
237
|
####################
|
|
219
238
|
|
|
220
|
-
def
|
|
239
|
+
def begin_transaction(self) -> Any:
|
|
221
240
|
"""Start transaction"""
|
|
222
241
|
raise Exception("Not implemented")
|
|
223
242
|
|
|
224
|
-
def
|
|
243
|
+
def commit_transaction(self) -> Any:
|
|
225
244
|
"""Commit transaction"""
|
|
226
245
|
raise Exception("Not implemented")
|
|
227
246
|
|
|
228
|
-
def
|
|
247
|
+
def rollback_transaction(self) -> Any:
|
|
229
248
|
"""Rollback transaction"""
|
|
230
249
|
raise Exception("Not implemented")
|
|
231
250
|
|
|
232
251
|
# @contextmanager
|
|
233
|
-
def transaction(self,
|
|
252
|
+
def transaction(self, db_conn: Any = None) -> Any:
|
|
234
253
|
"""
|
|
235
254
|
Transaction context manager
|
|
236
255
|
|
|
@@ -243,11 +262,11 @@ class DatabaseBackend:
|
|
|
243
262
|
### Data ###
|
|
244
263
|
############
|
|
245
264
|
|
|
246
|
-
def
|
|
265
|
+
def last_insert_id(self) -> int:
|
|
247
266
|
"""Get last inserted row id generated by auto increment"""
|
|
248
267
|
raise Exception("Not implemented")
|
|
249
268
|
|
|
250
|
-
def
|
|
269
|
+
def affected_rows(self) -> int:
|
|
251
270
|
"""Get affected rows count"""
|
|
252
271
|
raise Exception("Not implemented")
|
|
253
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()
|