database-wrapper 0.1.32__py3-none-any.whl → 0.1.37__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/__init__.py +2 -3
- database_wrapper/common.py +14 -0
- database_wrapper/config.py +3 -3
- database_wrapper/db_data_model.py +50 -3
- database_wrapper/db_wrapper.py +71 -296
- database_wrapper/db_wrapper_async.py +69 -295
- database_wrapper/db_wrapper_mixin.py +308 -0
- database_wrapper/utils/dataclass_addons.py +3 -3
- {database_wrapper-0.1.32.dist-info → database_wrapper-0.1.37.dist-info}/METADATA +5 -5
- database_wrapper-0.1.37.dist-info/RECORD +16 -0
- {database_wrapper-0.1.32.dist-info → database_wrapper-0.1.37.dist-info}/WHEEL +1 -1
- database_wrapper/db_wrapper_interface.py +0 -454
- database_wrapper-0.1.32.dist-info/RECORD +0 -15
- {database_wrapper-0.1.32.dist-info → database_wrapper-0.1.37.dist-info}/top_level.txt +0 -0
database_wrapper/db_wrapper.py
CHANGED
|
@@ -1,103 +1,31 @@
|
|
|
1
|
-
import
|
|
1
|
+
from typing import Generator, Any, overload
|
|
2
2
|
|
|
3
|
-
from typing import Generator, cast, Any, overload
|
|
4
|
-
|
|
5
|
-
from .db_backend import DatabaseBackend
|
|
6
3
|
from .db_data_model import DBDataModel
|
|
7
|
-
from .
|
|
4
|
+
from .db_wrapper_mixin import DBWrapperMixin
|
|
5
|
+
from .common import OrderByItem, DataModelType
|
|
8
6
|
|
|
9
7
|
|
|
10
|
-
class DBWrapper(
|
|
8
|
+
class DBWrapper(DBWrapperMixin):
|
|
11
9
|
"""
|
|
12
10
|
Database wrapper class.
|
|
13
11
|
"""
|
|
14
12
|
|
|
15
|
-
###########################
|
|
16
|
-
### Instance properties ###
|
|
17
|
-
###########################
|
|
18
|
-
|
|
19
|
-
# Db backend
|
|
20
|
-
db: Any
|
|
21
|
-
"""Database backend object"""
|
|
22
|
-
|
|
23
|
-
dbConn: Any
|
|
24
|
-
"""
|
|
25
|
-
Database connection object.
|
|
26
|
-
|
|
27
|
-
Its not always set. Currently is used as a placeholder for async connections.
|
|
28
|
-
For sync connections db - DatabaseBackend.connection is used.
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
# logger
|
|
32
|
-
logger: Any
|
|
33
|
-
"""Logger object"""
|
|
34
|
-
|
|
35
13
|
#######################
|
|
36
14
|
### Class lifecycle ###
|
|
37
15
|
#######################
|
|
38
16
|
|
|
39
|
-
# Meta methods
|
|
40
|
-
def __init__(
|
|
41
|
-
self,
|
|
42
|
-
db: DatabaseBackend,
|
|
43
|
-
dbConn: Any = None,
|
|
44
|
-
logger: logging.Logger | None = None,
|
|
45
|
-
):
|
|
46
|
-
"""
|
|
47
|
-
Initializes a new instance of the DBWrapper class.
|
|
48
|
-
|
|
49
|
-
Args:
|
|
50
|
-
db (DatabaseBackend): The DatabaseBackend object.
|
|
51
|
-
logger (logging.Logger, optional): The logger object. Defaults to None.
|
|
52
|
-
"""
|
|
53
|
-
self.db = db
|
|
54
|
-
self.dbConn = dbConn
|
|
55
|
-
|
|
56
|
-
if logger is None:
|
|
57
|
-
loggerName = f"{__name__}.{self.__class__.__name__}"
|
|
58
|
-
self.logger = logging.getLogger(loggerName)
|
|
59
|
-
else:
|
|
60
|
-
self.logger = logger
|
|
61
|
-
|
|
62
|
-
def __del__(self):
|
|
63
|
-
"""
|
|
64
|
-
Deallocates the instance of the DBWrapper class.
|
|
65
|
-
"""
|
|
66
|
-
self.logger.debug("Dealloc")
|
|
67
|
-
self.close()
|
|
68
|
-
|
|
69
17
|
def close(self) -> None:
|
|
70
18
|
"""
|
|
71
|
-
Close resources. Usually you should not close connections here,
|
|
19
|
+
Close resources. Usually you should not close connections here,
|
|
20
|
+
just remove references.
|
|
72
21
|
"""
|
|
73
22
|
|
|
74
|
-
|
|
75
|
-
if hasattr(self, "db") and self.db:
|
|
76
|
-
del self.db
|
|
77
|
-
|
|
78
|
-
if hasattr(self, "dbConn") and self.dbConn:
|
|
79
|
-
del self.dbConn
|
|
23
|
+
raise NotImplementedError("Method not implemented")
|
|
80
24
|
|
|
81
25
|
######################
|
|
82
26
|
### Helper methods ###
|
|
83
27
|
######################
|
|
84
28
|
|
|
85
|
-
def makeIdentifier(self, schema: str | None, name: str) -> Any:
|
|
86
|
-
"""
|
|
87
|
-
Creates a SQL identifier object from the given name.
|
|
88
|
-
|
|
89
|
-
Args:
|
|
90
|
-
schema (str | None): The schema to create the identifier from.
|
|
91
|
-
name (str): The name to create the identifier from.
|
|
92
|
-
|
|
93
|
-
Returns:
|
|
94
|
-
str: The created SQL identifier object.
|
|
95
|
-
"""
|
|
96
|
-
if schema:
|
|
97
|
-
return f"{schema}.{name}"
|
|
98
|
-
|
|
99
|
-
return name
|
|
100
|
-
|
|
101
29
|
@overload
|
|
102
30
|
def createCursor(self) -> Any: ...
|
|
103
31
|
|
|
@@ -109,7 +37,7 @@ class DBWrapper(DBWrapperInterface):
|
|
|
109
37
|
Creates a new cursor object.
|
|
110
38
|
|
|
111
39
|
Args:
|
|
112
|
-
emptyDataClass (
|
|
40
|
+
emptyDataClass (DataModelType | None, optional): The data model to use for the cursor. Defaults to None.
|
|
113
41
|
|
|
114
42
|
Returns:
|
|
115
43
|
The created cursor object.
|
|
@@ -117,88 +45,27 @@ class DBWrapper(DBWrapperInterface):
|
|
|
117
45
|
assert self.db is not None, "Database connection is not set"
|
|
118
46
|
return self.db.cursor
|
|
119
47
|
|
|
120
|
-
def logQuery(self, cursor: Any, query: Any, params: tuple[Any, ...]) -> None:
|
|
121
|
-
"""
|
|
122
|
-
Logs the given query and parameters.
|
|
123
|
-
|
|
124
|
-
Args:
|
|
125
|
-
cursor (Any): The database cursor.
|
|
126
|
-
query (Any): The query to log.
|
|
127
|
-
params (tuple[Any, ...]): The parameters to log.
|
|
128
|
-
"""
|
|
129
|
-
self.logger.debug(f"Query: {query} with params: {params}")
|
|
130
|
-
|
|
131
|
-
def turnDataIntoModel(
|
|
132
|
-
self,
|
|
133
|
-
emptyDataClass: T,
|
|
134
|
-
dbData: dict[str, Any],
|
|
135
|
-
) -> T:
|
|
136
|
-
"""
|
|
137
|
-
Turns the given data into a data model.
|
|
138
|
-
By default we are pretty sure that there is no factory in the cursor,
|
|
139
|
-
So we need to create a new instance of the data model and fill it with data
|
|
140
|
-
|
|
141
|
-
Args:
|
|
142
|
-
emptyDataClass (T): The data model to use.
|
|
143
|
-
dbData (dict[str, Any]): The data to turn into a model.
|
|
144
|
-
|
|
145
|
-
Returns:
|
|
146
|
-
T: The data model filled with data.
|
|
147
|
-
"""
|
|
148
|
-
|
|
149
|
-
result = emptyDataClass.__class__()
|
|
150
|
-
result.fillDataFromDict(dbData)
|
|
151
|
-
result.raw_data = dbData
|
|
152
|
-
return result
|
|
153
|
-
|
|
154
48
|
#####################
|
|
155
49
|
### Query methods ###
|
|
156
50
|
#####################
|
|
157
51
|
|
|
158
|
-
def filterQuery(self, schemaName: str | None, tableName: str) -> Any:
|
|
159
|
-
"""
|
|
160
|
-
Creates a SQL query to filter data from the given table.
|
|
161
|
-
|
|
162
|
-
Args:
|
|
163
|
-
schemaName (str | None): The name of the schema to filter data from.
|
|
164
|
-
tableName (str): The name of the table to filter data from.
|
|
165
|
-
|
|
166
|
-
Returns:
|
|
167
|
-
Any: The created SQL query object.
|
|
168
|
-
"""
|
|
169
|
-
fullTableName = self.makeIdentifier(schemaName, tableName)
|
|
170
|
-
return f"SELECT * FROM {fullTableName}"
|
|
171
|
-
|
|
172
|
-
def limitQuery(self, offset: int = 0, limit: int = 100) -> Any:
|
|
173
|
-
"""
|
|
174
|
-
Creates a SQL query to limit the number of results returned.
|
|
175
|
-
|
|
176
|
-
Args:
|
|
177
|
-
offset (int, optional): The number of results to skip. Defaults to 0.
|
|
178
|
-
limit (int, optional): The maximum number of results to return. Defaults to 100.
|
|
179
|
-
|
|
180
|
-
Returns:
|
|
181
|
-
Any: The created SQL query object.
|
|
182
|
-
"""
|
|
183
|
-
return f"LIMIT {limit} OFFSET {offset}"
|
|
184
|
-
|
|
185
52
|
# Action methods
|
|
186
53
|
def getOne(
|
|
187
54
|
self,
|
|
188
|
-
emptyDataClass:
|
|
55
|
+
emptyDataClass: DataModelType,
|
|
189
56
|
customQuery: Any = None,
|
|
190
|
-
) ->
|
|
57
|
+
) -> DataModelType | None:
|
|
191
58
|
"""
|
|
192
|
-
Retrieves a single record from the database.
|
|
59
|
+
Retrieves a single record from the database by class defined id.
|
|
193
60
|
|
|
194
61
|
Args:
|
|
195
|
-
emptyDataClass (
|
|
62
|
+
emptyDataClass (DataModelType): The data model to use for the query.
|
|
196
63
|
customQuery (Any, optional): The custom query to use for the query. Defaults to None.
|
|
197
64
|
|
|
198
65
|
Returns:
|
|
199
|
-
|
|
66
|
+
DataModelType | None: The result of the query.
|
|
200
67
|
"""
|
|
201
|
-
# Query
|
|
68
|
+
# Query and filter
|
|
202
69
|
_query = (
|
|
203
70
|
customQuery
|
|
204
71
|
or emptyDataClass.queryBase()
|
|
@@ -211,18 +78,21 @@ class DBWrapper(DBWrapperInterface):
|
|
|
211
78
|
if not idValue:
|
|
212
79
|
raise ValueError("Id value is not set")
|
|
213
80
|
|
|
81
|
+
_filter = f"WHERE {self.makeIdentifier(emptyDataClass.tableAlias, idKey)} = %s"
|
|
82
|
+
_params = (idValue,)
|
|
83
|
+
|
|
214
84
|
# Create a SQL object for the query and format it
|
|
215
|
-
querySql =
|
|
85
|
+
querySql = self._formatFilterQuery(_query, _filter, None, None)
|
|
216
86
|
|
|
217
87
|
# Create a new cursor
|
|
218
88
|
newCursor = self.createCursor(emptyDataClass)
|
|
219
89
|
|
|
220
90
|
# Log
|
|
221
|
-
self.logQuery(newCursor, querySql,
|
|
91
|
+
self.logQuery(newCursor, querySql, _params)
|
|
222
92
|
|
|
223
93
|
# Load data
|
|
224
94
|
try:
|
|
225
|
-
newCursor.execute(querySql,
|
|
95
|
+
newCursor.execute(querySql, _params)
|
|
226
96
|
|
|
227
97
|
# Fetch one row
|
|
228
98
|
row = newCursor.fetchone()
|
|
@@ -237,42 +107,44 @@ class DBWrapper(DBWrapperInterface):
|
|
|
237
107
|
|
|
238
108
|
def getByKey(
|
|
239
109
|
self,
|
|
240
|
-
emptyDataClass:
|
|
110
|
+
emptyDataClass: DataModelType,
|
|
241
111
|
idKey: str,
|
|
242
112
|
idValue: Any,
|
|
243
113
|
customQuery: Any = None,
|
|
244
|
-
) ->
|
|
114
|
+
) -> DataModelType | None:
|
|
245
115
|
"""
|
|
246
116
|
Retrieves a single record from the database using the given key.
|
|
247
117
|
|
|
248
118
|
Args:
|
|
249
|
-
emptyDataClass (
|
|
119
|
+
emptyDataClass (DataModelType): The data model to use for the query.
|
|
250
120
|
idKey (str): The name of the key to use for the query.
|
|
251
121
|
idValue (Any): The value of the key to use for the query.
|
|
252
122
|
customQuery (Any, optional): The custom query to use for the query. Defaults to None.
|
|
253
123
|
|
|
254
124
|
Returns:
|
|
255
|
-
|
|
125
|
+
DataModelType | None: The result of the query.
|
|
256
126
|
"""
|
|
257
|
-
# Query
|
|
127
|
+
# Query and filter
|
|
258
128
|
_query = (
|
|
259
129
|
customQuery
|
|
260
130
|
or emptyDataClass.queryBase()
|
|
261
131
|
or self.filterQuery(emptyDataClass.schemaName, emptyDataClass.tableName)
|
|
262
132
|
)
|
|
133
|
+
_filter = f"WHERE {self.makeIdentifier(emptyDataClass.tableAlias, idKey)} = %s"
|
|
134
|
+
_params = (idValue,)
|
|
263
135
|
|
|
264
136
|
# Create a SQL object for the query and format it
|
|
265
|
-
querySql =
|
|
137
|
+
querySql = self._formatFilterQuery(_query, _filter, None, None)
|
|
266
138
|
|
|
267
139
|
# Create a new cursor
|
|
268
140
|
newCursor = self.createCursor(emptyDataClass)
|
|
269
141
|
|
|
270
142
|
# Log
|
|
271
|
-
self.logQuery(newCursor, querySql,
|
|
143
|
+
self.logQuery(newCursor, querySql, _params)
|
|
272
144
|
|
|
273
145
|
# Load data
|
|
274
146
|
try:
|
|
275
|
-
newCursor.execute(querySql,
|
|
147
|
+
newCursor.execute(querySql, _params)
|
|
276
148
|
|
|
277
149
|
# Fetch one row
|
|
278
150
|
row = newCursor.fetchone()
|
|
@@ -288,19 +160,19 @@ class DBWrapper(DBWrapperInterface):
|
|
|
288
160
|
|
|
289
161
|
def getAll(
|
|
290
162
|
self,
|
|
291
|
-
emptyDataClass:
|
|
163
|
+
emptyDataClass: DataModelType,
|
|
292
164
|
idKey: str | None = None,
|
|
293
165
|
idValue: Any | None = None,
|
|
294
166
|
orderBy: OrderByItem | None = None,
|
|
295
167
|
offset: int = 0,
|
|
296
168
|
limit: int = 100,
|
|
297
169
|
customQuery: Any = None,
|
|
298
|
-
) -> Generator[
|
|
170
|
+
) -> Generator[DataModelType, None, None]:
|
|
299
171
|
"""
|
|
300
172
|
Retrieves all records from the database.
|
|
301
173
|
|
|
302
174
|
Args:
|
|
303
|
-
emptyDataClass (
|
|
175
|
+
emptyDataClass (DataModelType): The data model to use for the query.
|
|
304
176
|
idKey (str | None, optional): The name of the key to use for filtering. Defaults to None.
|
|
305
177
|
idValue (Any | None, optional): The value of the key to use for filtering. Defaults to None.
|
|
306
178
|
orderBy (OrderByItem | None, optional): The order by item to use for sorting. Defaults to None.
|
|
@@ -309,36 +181,28 @@ class DBWrapper(DBWrapperInterface):
|
|
|
309
181
|
customQuery (Any, optional): The custom query to use for the query. Defaults to None.
|
|
310
182
|
|
|
311
183
|
Returns:
|
|
312
|
-
Generator[
|
|
184
|
+
Generator[DataModelType, None, None]: The result of the query.
|
|
313
185
|
"""
|
|
314
|
-
# Query
|
|
186
|
+
# Query and filter
|
|
315
187
|
_query = (
|
|
316
188
|
customQuery
|
|
317
189
|
or emptyDataClass.queryBase()
|
|
318
190
|
or self.filterQuery(emptyDataClass.schemaName, emptyDataClass.tableName)
|
|
319
191
|
)
|
|
320
192
|
_params: tuple[Any, ...] = ()
|
|
321
|
-
|
|
322
|
-
# Filter
|
|
193
|
+
_filter = ""
|
|
323
194
|
if idKey and idValue:
|
|
324
|
-
|
|
195
|
+
_filter = (
|
|
196
|
+
f"WHERE {self.makeIdentifier(emptyDataClass.tableAlias, idKey)} = %s"
|
|
197
|
+
)
|
|
325
198
|
_params = (idValue,)
|
|
326
199
|
|
|
327
|
-
#
|
|
328
|
-
_order =
|
|
329
|
-
_limit =
|
|
330
|
-
|
|
331
|
-
if orderBy:
|
|
332
|
-
orderList = [
|
|
333
|
-
f"{item[0]} {item[1] if len(item) > 1 and item[1] != None else 'ASC'}"
|
|
334
|
-
for item in orderBy
|
|
335
|
-
]
|
|
336
|
-
_order = "ORDER BY %s" % ", ".join(orderList)
|
|
337
|
-
if offset or limit:
|
|
338
|
-
_limit = f"{self.limitQuery(offset, limit)}"
|
|
200
|
+
# Order and limit
|
|
201
|
+
_order = self.orderQuery(orderBy)
|
|
202
|
+
_limit = self.limitQuery(offset, limit)
|
|
339
203
|
|
|
340
204
|
# Create a SQL object for the query and format it
|
|
341
|
-
querySql =
|
|
205
|
+
querySql = self._formatFilterQuery(_query, _filter, _order, _limit)
|
|
342
206
|
|
|
343
207
|
# Create a new cursor
|
|
344
208
|
newCursor = self.createCursor(emptyDataClass)
|
|
@@ -356,114 +220,36 @@ class DBWrapper(DBWrapperInterface):
|
|
|
356
220
|
row = newCursor.fetchone()
|
|
357
221
|
if row is None:
|
|
358
222
|
break
|
|
223
|
+
|
|
359
224
|
yield self.turnDataIntoModel(emptyDataClass, row)
|
|
360
225
|
|
|
361
226
|
finally:
|
|
362
227
|
# Ensure the cursor is closed after the generator is exhausted or an error occurs
|
|
363
228
|
newCursor.close()
|
|
364
229
|
|
|
365
|
-
def formatFilter(self, key: str, filter: Any) -> tuple[Any, ...]:
|
|
366
|
-
if type(filter) is dict:
|
|
367
|
-
if "$contains" in filter:
|
|
368
|
-
return (
|
|
369
|
-
f"{key} LIKE %s",
|
|
370
|
-
f"%{filter['$contains']}%",
|
|
371
|
-
)
|
|
372
|
-
elif "$starts_with" in filter:
|
|
373
|
-
return (f"{key} LIKE %s", f"{filter['$starts_with']}%")
|
|
374
|
-
elif "$ends_with" in filter:
|
|
375
|
-
return (f"{key} LIKE %s", f"%{filter['$ends_with']}")
|
|
376
|
-
elif "$min" in filter and "$max" not in filter:
|
|
377
|
-
return (f"{key} >= %s", filter["$min"]) # type: ignore
|
|
378
|
-
elif "$max" in filter and "$min" not in filter:
|
|
379
|
-
return (f"{key} <= %s", filter["$max"]) # type: ignore
|
|
380
|
-
elif "$min" in filter and "$max" in filter:
|
|
381
|
-
return (f"{key} BETWEEN %s AND %s", filter["$min"], filter["$max"]) # type: ignore
|
|
382
|
-
elif "$in" in filter:
|
|
383
|
-
inFilter1: list[Any] = cast(list[Any], filter["$in"])
|
|
384
|
-
return (f"{key} IN (%s)" % ",".join(["%s"] * len(inFilter1)),) + tuple(
|
|
385
|
-
inFilter1
|
|
386
|
-
)
|
|
387
|
-
elif "$not_in" in filter:
|
|
388
|
-
inFilter2: list[Any] = cast(list[Any], filter["$in"])
|
|
389
|
-
return (
|
|
390
|
-
f"{key} NOT IN (%s)" % ",".join(["%s"] * len(inFilter2)),
|
|
391
|
-
) + tuple(inFilter2)
|
|
392
|
-
elif "$not" in filter:
|
|
393
|
-
return (f"{key} != %s", filter["$not"]) # type: ignore
|
|
394
|
-
|
|
395
|
-
elif "$gt" in filter:
|
|
396
|
-
return (f"{key} > %s", filter["$gt"]) # type: ignore
|
|
397
|
-
elif "$gte" in filter:
|
|
398
|
-
return (f"{key} >= %s", filter["$gte"]) # type: ignore
|
|
399
|
-
elif "$lt" in filter:
|
|
400
|
-
return (f"{key} < %s", filter["$lt"]) # type: ignore
|
|
401
|
-
elif "$lte" in filter:
|
|
402
|
-
return (f"{key} <= %s", filter["$lte"]) # type: ignore
|
|
403
|
-
elif "$is_null" in filter:
|
|
404
|
-
return (f"{key} IS NULL",) # type: ignore
|
|
405
|
-
elif "$is_not_null" in filter:
|
|
406
|
-
return (f"{key} IS NOT NULL",) # type: ignore
|
|
407
|
-
|
|
408
|
-
raise NotImplementedError("Filter type not supported")
|
|
409
|
-
elif type(filter) is str or type(filter) is int or type(filter) is float:
|
|
410
|
-
return (f"{key} = %s", filter)
|
|
411
|
-
elif type(filter) is bool:
|
|
412
|
-
return (
|
|
413
|
-
f"{key} = TRUE" if filter else f"{key} = FALSE",
|
|
414
|
-
NoParam,
|
|
415
|
-
)
|
|
416
|
-
else:
|
|
417
|
-
raise NotImplementedError(
|
|
418
|
-
f"Filter type not supported: {key} = {type(filter)}"
|
|
419
|
-
)
|
|
420
|
-
|
|
421
|
-
def createFilter(
|
|
422
|
-
self, filter: dict[str, Any] | None
|
|
423
|
-
) -> tuple[str, tuple[Any, ...]]:
|
|
424
|
-
if filter is None or len(filter) == 0:
|
|
425
|
-
return ("", tuple())
|
|
426
|
-
|
|
427
|
-
raw = [self.formatFilter(key, filter[key]) for key in filter]
|
|
428
|
-
_query = " AND ".join([tup[0] for tup in raw])
|
|
429
|
-
_query = f"WHERE {_query}"
|
|
430
|
-
_params = tuple([val for tup in raw for val in tup[1:] if val is not NoParam])
|
|
431
|
-
|
|
432
|
-
return (_query, _params)
|
|
433
|
-
|
|
434
230
|
def getFiltered(
|
|
435
231
|
self,
|
|
436
|
-
emptyDataClass:
|
|
232
|
+
emptyDataClass: DataModelType,
|
|
437
233
|
filter: dict[str, Any],
|
|
438
234
|
orderBy: OrderByItem | None = None,
|
|
439
235
|
offset: int = 0,
|
|
440
236
|
limit: int = 100,
|
|
441
237
|
customQuery: Any = None,
|
|
442
|
-
) -> Generator[
|
|
443
|
-
#
|
|
238
|
+
) -> Generator[DataModelType, None, None]:
|
|
239
|
+
# Query and filter
|
|
444
240
|
_query = (
|
|
445
241
|
customQuery
|
|
446
242
|
or emptyDataClass.queryBase()
|
|
447
243
|
or self.filterQuery(emptyDataClass.schemaName, emptyDataClass.tableName)
|
|
448
244
|
)
|
|
449
245
|
(_filter, _params) = self.createFilter(filter)
|
|
450
|
-
_filter = _filter
|
|
451
|
-
|
|
452
|
-
# Limits
|
|
453
|
-
_order = ""
|
|
454
|
-
_limit = ""
|
|
455
246
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
for item in orderBy
|
|
460
|
-
]
|
|
461
|
-
_order = "ORDER BY %s" % ", ".join(orderList)
|
|
462
|
-
if offset or limit:
|
|
463
|
-
_limit = f"{self.limitQuery(offset, limit)}"
|
|
247
|
+
# Order and limit
|
|
248
|
+
_order = self.orderQuery(orderBy)
|
|
249
|
+
_limit = self.limitQuery(offset, limit)
|
|
464
250
|
|
|
465
|
-
# Create
|
|
466
|
-
querySql =
|
|
251
|
+
# Create SQL query
|
|
252
|
+
querySql = self._formatFilterQuery(_query, _filter, _order, _limit)
|
|
467
253
|
|
|
468
254
|
# Create a new cursor
|
|
469
255
|
newCursor = self.createCursor(emptyDataClass)
|
|
@@ -481,6 +267,7 @@ class DBWrapper(DBWrapperInterface):
|
|
|
481
267
|
row = newCursor.fetchone()
|
|
482
268
|
if row is None:
|
|
483
269
|
break
|
|
270
|
+
|
|
484
271
|
yield self.turnDataIntoModel(emptyDataClass, row)
|
|
485
272
|
|
|
486
273
|
finally:
|
|
@@ -508,20 +295,10 @@ class DBWrapper(DBWrapperInterface):
|
|
|
508
295
|
Returns:
|
|
509
296
|
tuple[int, int]: The id of the record and the number of affected rows.
|
|
510
297
|
"""
|
|
511
|
-
keys = storeData.keys()
|
|
512
298
|
values = list(storeData.values())
|
|
513
|
-
|
|
514
299
|
tableIdentifier = self.makeIdentifier(schemaName, tableName)
|
|
515
300
|
returnKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
|
|
516
|
-
|
|
517
|
-
columns = ", ".join(keys)
|
|
518
|
-
valuesPlaceholder = ", ".join(["%s"] * len(values))
|
|
519
|
-
insertQuery = (
|
|
520
|
-
f"INSERT INTO {tableIdentifier} "
|
|
521
|
-
f"({columns}) "
|
|
522
|
-
f"VALUES ({valuesPlaceholder}) "
|
|
523
|
-
f"RETURNING {returnKey}"
|
|
524
|
-
)
|
|
301
|
+
insertQuery = self._formatInsertQuery(tableIdentifier, storeData, returnKey)
|
|
525
302
|
|
|
526
303
|
# Create a new cursor
|
|
527
304
|
newCursor = self.createCursor(emptyDataClass)
|
|
@@ -545,18 +322,21 @@ class DBWrapper(DBWrapperInterface):
|
|
|
545
322
|
newCursor.close()
|
|
546
323
|
|
|
547
324
|
@overload
|
|
548
|
-
def store(self, records:
|
|
325
|
+
def store(self, records: DataModelType) -> tuple[int, int]: # type: ignore
|
|
549
326
|
...
|
|
550
327
|
|
|
551
328
|
@overload
|
|
552
|
-
def store(self, records: list[
|
|
329
|
+
def store(self, records: list[DataModelType]) -> list[tuple[int, int]]: ...
|
|
553
330
|
|
|
554
|
-
def store(
|
|
331
|
+
def store(
|
|
332
|
+
self,
|
|
333
|
+
records: DataModelType | list[DataModelType],
|
|
334
|
+
) -> tuple[int, int] | list[tuple[int, int]]:
|
|
555
335
|
"""
|
|
556
336
|
Stores a record or a list of records in the database.
|
|
557
337
|
|
|
558
338
|
Args:
|
|
559
|
-
records (
|
|
339
|
+
records (DataModelType | list[DataModelType]): The record or records to store.
|
|
560
340
|
|
|
561
341
|
Returns:
|
|
562
342
|
tuple[int, int] | list[tuple[int, int]]: The id of the record and
|
|
@@ -615,17 +395,12 @@ class DBWrapper(DBWrapperInterface):
|
|
|
615
395
|
int: The number of affected rows.
|
|
616
396
|
"""
|
|
617
397
|
(idKey, idValue) = updateId
|
|
618
|
-
keys = updateData.keys()
|
|
619
398
|
values = list(updateData.values())
|
|
620
399
|
values.append(idValue)
|
|
621
400
|
|
|
622
|
-
set_clause = ", ".join(f"{key} = %s" for key in keys)
|
|
623
|
-
|
|
624
401
|
tableIdentifier = self.makeIdentifier(schemaName, tableName)
|
|
625
402
|
updateKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
|
|
626
|
-
updateQuery = (
|
|
627
|
-
f"UPDATE {tableIdentifier} SET {set_clause} WHERE {updateKey} = %s"
|
|
628
|
-
)
|
|
403
|
+
updateQuery = self._formatUpdateQuery(tableIdentifier, updateKey, updateData)
|
|
629
404
|
|
|
630
405
|
# Create a new cursor
|
|
631
406
|
newCursor = self.createCursor(emptyDataClass)
|
|
@@ -645,18 +420,18 @@ class DBWrapper(DBWrapperInterface):
|
|
|
645
420
|
newCursor.close()
|
|
646
421
|
|
|
647
422
|
@overload
|
|
648
|
-
def update(self, records:
|
|
423
|
+
def update(self, records: DataModelType) -> int: # type: ignore
|
|
649
424
|
...
|
|
650
425
|
|
|
651
426
|
@overload
|
|
652
|
-
def update(self, records: list[
|
|
427
|
+
def update(self, records: list[DataModelType]) -> list[int]: ...
|
|
653
428
|
|
|
654
|
-
def update(self, records:
|
|
429
|
+
def update(self, records: DataModelType | list[DataModelType]) -> int | list[int]:
|
|
655
430
|
"""
|
|
656
431
|
Updates a record or a list of records in the database.
|
|
657
432
|
|
|
658
433
|
Args:
|
|
659
|
-
records (
|
|
434
|
+
records (DataModelType | list[DataModelType]): The record or records to update.
|
|
660
435
|
|
|
661
436
|
Returns:
|
|
662
437
|
int | list[int]: The number of affected rows for a single record or a list of
|
|
@@ -739,7 +514,7 @@ class DBWrapper(DBWrapperInterface):
|
|
|
739
514
|
|
|
740
515
|
tableIdentifier = self.makeIdentifier(schemaName, tableName)
|
|
741
516
|
deleteKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
|
|
742
|
-
delete_query =
|
|
517
|
+
delete_query = self._formatDeleteQuery(tableIdentifier, deleteKey)
|
|
743
518
|
|
|
744
519
|
# Create a new cursor
|
|
745
520
|
newCursor = self.createCursor(emptyDataClass)
|
|
@@ -759,18 +534,18 @@ class DBWrapper(DBWrapperInterface):
|
|
|
759
534
|
newCursor.close()
|
|
760
535
|
|
|
761
536
|
@overload
|
|
762
|
-
def delete(self, records:
|
|
537
|
+
def delete(self, records: DataModelType) -> int: # type: ignore
|
|
763
538
|
...
|
|
764
539
|
|
|
765
540
|
@overload
|
|
766
|
-
def delete(self, records: list[
|
|
541
|
+
def delete(self, records: list[DataModelType]) -> list[int]: ...
|
|
767
542
|
|
|
768
|
-
def delete(self, records:
|
|
543
|
+
def delete(self, records: DataModelType | list[DataModelType]) -> int | list[int]:
|
|
769
544
|
"""
|
|
770
545
|
Deletes a record or a list of records from the database.
|
|
771
546
|
|
|
772
547
|
Args:
|
|
773
|
-
records (
|
|
548
|
+
records (DataModelType | list[DataModelType]): The record or records to delete.
|
|
774
549
|
|
|
775
550
|
Returns:
|
|
776
551
|
int | list[int]: The number of affected rows for a single record or a list of
|