tina4-python 0.2.122__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 (47) hide show
  1. tina4_python/Auth.py +222 -0
  2. tina4_python/Constant.py +43 -0
  3. tina4_python/Database.py +591 -0
  4. tina4_python/DatabaseResult.py +107 -0
  5. tina4_python/DatabaseTypes.py +15 -0
  6. tina4_python/Debug.py +126 -0
  7. tina4_python/Env.py +37 -0
  8. tina4_python/Localization.py +42 -0
  9. tina4_python/Messages.py +30 -0
  10. tina4_python/MiddleWare.py +90 -0
  11. tina4_python/Migration.py +107 -0
  12. tina4_python/ORM.py +639 -0
  13. tina4_python/Queue.py +615 -0
  14. tina4_python/Request.py +19 -0
  15. tina4_python/Response.py +121 -0
  16. tina4_python/Router.py +423 -0
  17. tina4_python/Session.py +342 -0
  18. tina4_python/ShellColors.py +20 -0
  19. tina4_python/Swagger.py +228 -0
  20. tina4_python/Template.py +107 -0
  21. tina4_python/Webserver.py +429 -0
  22. tina4_python/Websocket.py +49 -0
  23. tina4_python/__init__.py +392 -0
  24. tina4_python/messages.pot +83 -0
  25. tina4_python/public/css/readme.md +0 -0
  26. tina4_python/public/favicon.ico +0 -0
  27. tina4_python/public/images/403.png +0 -0
  28. tina4_python/public/images/404.png +0 -0
  29. tina4_python/public/images/500.png +0 -0
  30. tina4_python/public/images/logo.png +0 -0
  31. tina4_python/public/images/readme.md +0 -0
  32. tina4_python/public/js/readme.md +0 -0
  33. tina4_python/public/js/reconnecting-websocket.js +365 -0
  34. tina4_python/public/js/tina4helper.js +397 -0
  35. tina4_python/public/swagger/index.html +90 -0
  36. tina4_python/public/swagger/oauth2-redirect.html +63 -0
  37. tina4_python/templates/errors/403.twig +10 -0
  38. tina4_python/templates/errors/404.twig +10 -0
  39. tina4_python/templates/errors/500.twig +11 -0
  40. tina4_python/templates/readme.md +1 -0
  41. tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  42. tina4_python/translations/en/LC_MESSAGES/messages.po +80 -0
  43. tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  44. tina4_python/translations/fr/LC_MESSAGES/messages.po +84 -0
  45. tina4_python-0.2.122.dist-info/METADATA +465 -0
  46. tina4_python-0.2.122.dist-info/RECORD +47 -0
  47. tina4_python-0.2.122.dist-info/WHEEL +4 -0
tina4_python/ORM.py ADDED
@@ -0,0 +1,639 @@
1
+ #
2
+ # Tina4 - This is not a 4ramework.
3
+ # Copy-right 2007 - current Tina4
4
+ # License: MIT https://opensource.org/licenses/MIT
5
+ #
6
+ # flake8: noqa: E501
7
+ import base64
8
+ from datetime import datetime, date
9
+ import inspect
10
+ import ast
11
+ import json
12
+ import os
13
+ from tina4_python.Constant import TINA4_LOG_ERROR
14
+ from tina4_python.Debug import Debug
15
+ from tina4_python.DatabaseTypes import *
16
+ import inspect
17
+
18
+ def find_all_sub_classes(a_class):
19
+ return a_class.__subclasses__()
20
+
21
+
22
+ def orm(dba):
23
+ from tina4_python import root_path
24
+ Debug("Initializing ORM")
25
+ orm_path = root_path + os.sep + "src" + os.sep + "orm"
26
+ if not os.path.exists(orm_path):
27
+ os.makedirs(orm_path)
28
+
29
+ # load and assign
30
+ for file in os.listdir(orm_path):
31
+ if not file.endswith(".py"):
32
+ continue
33
+ mod_name = file.removesuffix(".py")
34
+ if "__init__" not in mod_name and "__pycache__" not in mod_name and ".git" not in mod_name:
35
+ # import and set the database object
36
+ try:
37
+ Debug('from src.orm.' + mod_name + ' import ' + mod_name)
38
+ exec('from src.orm.' + mod_name + ' import ' + mod_name+"\n"+mod_name + ".__dba__ = dba")
39
+ except Exception as e:
40
+ Debug("Failed to import " + mod_name, str(e))
41
+ classes = find_all_sub_classes(ORM)
42
+ for a_class in classes:
43
+ a_class.__dba__ = dba
44
+
45
+
46
+ class BaseField:
47
+ primary_key = False
48
+ column_type = None
49
+ default_value = None
50
+ column_name = None
51
+ auto_increment = False
52
+ value = None
53
+ field_size = None
54
+ decimal_places = None
55
+ protected_field = False
56
+
57
+ def get_definition(self, database_type="generic"):
58
+ return self.column_name.lower() + " not defined"
59
+
60
+ def __eq__(self, other):
61
+ if self.value is None:
62
+ self.value = self.default_value
63
+ return other == self.value
64
+
65
+ def __ne__(self, other):
66
+ if self.value is None:
67
+ self.value = self.default_value
68
+ return other != self.value
69
+
70
+ def __add__(self, other):
71
+ if self.value is None:
72
+ self.value = self.default_value
73
+ return other + self.value
74
+
75
+ def __mul__(self, other):
76
+ if self.value is None:
77
+ self.value = self.default_value
78
+ return other * self.value
79
+
80
+ def __sub__(self, other):
81
+ if self.value is None:
82
+ self.value = self.default_value
83
+ return self.value - other
84
+
85
+ def __truediv__(self, other):
86
+ if self.value is None:
87
+ self.value = self.default_value
88
+ return self.value / other
89
+
90
+ def __str__(self):
91
+ if self.value is None:
92
+ self.value = self.default_value
93
+ return str(self.value)
94
+
95
+ def __int__(self):
96
+ if self.value is None:
97
+ self.value = self.default_value
98
+ return int(self.value)
99
+
100
+ def __float__(self):
101
+ if self.value is None:
102
+ self.value = self.default_value
103
+ return float(self.value)
104
+
105
+ def __init__(self, column_name=None, primary_key=False, default_value=None, auto_increment=False, field_size=None,
106
+ decimal_places=None, protected_field=False):
107
+ self.primary_key = primary_key
108
+ self.column_type = None
109
+ if default_value is not None:
110
+ self.default_value = default_value
111
+ self.auto_increment = auto_increment
112
+ self.protected_field = protected_field
113
+ if field_size is not None:
114
+ self.field_size = field_size
115
+ if decimal_places is not None:
116
+ self.decimal_places = decimal_places
117
+
118
+ if column_name is None:
119
+ frame = inspect.stack()[1]
120
+ # Parse python syntax of the assignment line
121
+ st = ast.parse(frame.code_context[0].strip())
122
+ stmt = st.body[0]
123
+ # Assume class being instanced as simple assign statement
124
+ assert (isinstance(stmt, ast.Assign))
125
+ # Parse the target the class is assigned to
126
+ target = stmt.targets[0]
127
+ self.column_name = target.id
128
+ else:
129
+ self.column_name = column_name
130
+
131
+
132
+ class DateTimeField(BaseField):
133
+ column_type = datetime
134
+ default_value = datetime.now()
135
+
136
+ def get_definition(self, database_type="generic"):
137
+ if database_type == MSSQL:
138
+ return self.column_name.lower() + " datetime"
139
+ else:
140
+ return self.column_name.lower() + " timestamp"
141
+
142
+
143
+ class IntegerField(BaseField):
144
+ column_type = int
145
+ default_value = 0
146
+
147
+ def get_definition(self, database_type="generic"):
148
+ not_null = ""
149
+ if self.primary_key:
150
+ not_null = " not null"
151
+ if database_type == MSSQL:
152
+ return self.column_name.lower() + " integer identity(1,1) " + not_null
153
+ else:
154
+ return self.column_name.lower() + " integer default " + str(self.default_value) + " " + not_null
155
+
156
+
157
+ class NumericField(BaseField):
158
+ column_type = float
159
+ default_value = 0.00
160
+ field_size = 10
161
+ decimal_places = 2
162
+
163
+ def get_definition(self, database_type="generic"):
164
+ not_null = ""
165
+ if self.primary_key:
166
+ not_null = " not null"
167
+
168
+ return self.column_name.lower() + " numeric(" + str(self.field_size) + "," + str(
169
+ self.decimal_places) + ") default " + str(self.default_value) + not_null
170
+
171
+
172
+ class StringField(BaseField):
173
+ column_type = str
174
+ default_value = ""
175
+ field_size = 255
176
+
177
+ def get_definition(self, database_type="generic"):
178
+ not_null = ""
179
+ if self.primary_key:
180
+ not_null = " not null"
181
+
182
+ return self.column_name.lower() + " varchar(" + str(self.field_size) + ") default '" + str(
183
+ self.default_value) + "'" + not_null
184
+
185
+
186
+ class TextField(StringField):
187
+ pass
188
+
189
+
190
+ class BlobField(BaseField):
191
+ column_type = bytes
192
+ default_value = None
193
+
194
+ def get_definition(self, database_type="generic"):
195
+ field_type = "blob"
196
+ if database_type == MSSQL:
197
+ field_type = "varbinary(max)"
198
+ return self.column_name.lower() + " " + field_type
199
+
200
+
201
+ class ForeignKeyField:
202
+ field_type = None
203
+ references_table = None
204
+ references_column = None
205
+ primary_key = False
206
+ foreign_key = True
207
+ value = None
208
+ default_value = None
209
+ protected_field = False
210
+
211
+ def __eq__(self, other):
212
+ if self.value is None:
213
+ self.value = self.default_value
214
+ return other == self.value
215
+
216
+ def __ne__(self, other):
217
+ if self.value is None:
218
+ self.value = self.default_value
219
+ return other != self.value
220
+
221
+ def __add__(self, other):
222
+ if self.value is None:
223
+ self.value = self.default_value
224
+ return other + self.value
225
+
226
+ def __mul__(self, other):
227
+ if self.value is None:
228
+ self.value = self.default_value
229
+ return other * self.value
230
+
231
+ def __sub__(self, other):
232
+ if self.value is None:
233
+ self.value = self.default_value
234
+ return self.value - other
235
+
236
+ def __truediv__(self, other):
237
+ if self.value is None:
238
+ self.value = self.default_value
239
+ return self.value / other
240
+
241
+ def __str__(self):
242
+ if self.value is None:
243
+ self.value = self.default_value
244
+ return str(self.value)
245
+
246
+ def __init__(self, field_type=BaseField, references_table=None, column_name=None, default_value = None, protected_field=False):
247
+ self.field_type = field_type
248
+ self.references_table = references_table
249
+ self.references_column = field_type.column_name
250
+ self.default_value = default_value
251
+ self.auto_increment = False
252
+ self.protected_field = protected_field
253
+ self.value = field_type
254
+
255
+ if column_name is None:
256
+ frame = inspect.stack()[1]
257
+ # Parse python syntax of the assignment line
258
+ st = ast.parse(frame.code_context[0].strip())
259
+ stmt = st.body[0]
260
+ # Assume class being instanced as simple assign statement
261
+ assert (isinstance(stmt, ast.Assign))
262
+ # Parse the target the class is assigned to
263
+ target = stmt.targets[0]
264
+ self.column_name = target.id
265
+ else:
266
+ self.column_name = column_name
267
+
268
+ def get_definition(self, database_type="generic"):
269
+ references_definition = self.field_type.get_definition(database_type).split(" ")
270
+ # print("REFERENCES", references_definition, self.references_table.__table_name__, self.references_column)
271
+ return self.column_name + " " + str(references_definition[1]) + " references " + self.references_table.__table_name__ + "(" + self.references_column + ") on update cascade on delete cascade"
272
+
273
+
274
+ def json_serialize(obj):
275
+ """JSON serializer for objects not serializable by default json code"""
276
+ if isinstance(obj, (datetime, date)):
277
+ return obj.isoformat()
278
+ elif isinstance(obj, bytes):
279
+ return base64.b64encode(obj).decode('utf-8')
280
+ raise TypeError("Type %s not serializable" % type(obj))
281
+
282
+
283
+ class ORM:
284
+ __table_name__ = None
285
+ __dba__ = None
286
+ __field_definitions__ = {}
287
+
288
+ def __get_snake_case_name__(self, name):
289
+ """
290
+ Gets the table name
291
+ :param name:
292
+ :return:
293
+ """
294
+ if "_" in name:
295
+ return name.lower()
296
+ snake_case_name = ""
297
+ counter = 0
298
+ for c in name:
299
+ if c.isupper() and counter > 0:
300
+ snake_case_name = snake_case_name + "_" + c.lower()
301
+ else:
302
+ snake_case_name = snake_case_name + c.lower()
303
+ counter += 1
304
+ return snake_case_name
305
+
306
+ def __init__(self, init_object=None, table_name=None):
307
+ from tina4_python import root_path
308
+ # save the initial declarations
309
+ counter = 0
310
+ self.__field_definitions__ = {}
311
+ for key in dir(self):
312
+ if not key.startswith('__') and not key.startswith('_') and key not in ['save', 'load', 'delete', 'to_json',
313
+ 'to_dict', 'create_table', 'select', 'fetch', 'fetch_one']:
314
+ self.__field_definitions__[key] = getattr(self, key)
315
+ counter += 1
316
+
317
+ if counter == 0:
318
+ self.__field_definitions__["id"] = IntegerField(default_value=0, auto_increment=True, primary_key=True)
319
+
320
+ class_name = self.__class__.__name__
321
+ if self.__table_name__ is None:
322
+ if table_name is None:
323
+ self.__table_name__ = self.__get_snake_case_name__(class_name)
324
+ else:
325
+ self.__table_name__ = table_name.lower()
326
+
327
+ if init_object is not None:
328
+ self.__populate_orm(init_object)
329
+ else:
330
+ self.__populate_orm({})
331
+
332
+ # Debug("Checking for", self.__table_name__, TINA4_LOG_INFO)
333
+ if self.__dba__ is not None:
334
+ self.__table_exists = self.__dba__.table_exists(self.__table_name__)
335
+ if not self.__table_exists:
336
+ sql = self.__create_table__(self.__table_name__)
337
+ filename = root_path + os.sep + "migrations" + os.sep + "__" + self.__table_name__ + ".sql"
338
+ os.makedirs(os.path.dirname(filename), exist_ok=True)
339
+ #with open(filename, "w") as f:
340
+ # f.write(sql)
341
+ # f.close()
342
+ Debug.warning("Create Table ? ", sql)
343
+ else:
344
+ self.__table_exists = False
345
+
346
+
347
+
348
+ def __populate_orm(self, init_object):
349
+ """
350
+ Populates an ORM object from an input object, also transforms camel case objects to snake case ...
351
+ :param init_object:
352
+ :return:
353
+ """
354
+ for field, field_definition in self.__field_definitions__.items():
355
+ if hasattr(self, field):
356
+ setattr(self, field, None)
357
+ try:
358
+ field_definition.value = None
359
+ setattr(self, field, field_definition)
360
+ except Exception as e:
361
+ print("Could not set attribute for", field, str(e))
362
+
363
+ if isinstance(init_object, str):
364
+ init_object = json.loads(init_object)
365
+
366
+ for key, value in init_object.items():
367
+ snake_case_name = self.__get_snake_case_name__(key)
368
+ if snake_case_name in self.__field_definitions__:
369
+ try:
370
+ field_value = self.__field_definitions__[snake_case_name]
371
+ field_value.value = value
372
+ if hasattr(self, snake_case_name):
373
+ setattr(self, snake_case_name, field_value)
374
+ except Exception as e:
375
+ print("Could not set value for", snake_case_name, str(e))
376
+
377
+
378
+ def __get_primary_keys(self):
379
+ primary_keys = []
380
+ for key, value in self.__field_definitions__.items():
381
+ if value.primary_key:
382
+ primary_keys.append(key)
383
+
384
+ return primary_keys
385
+
386
+ def to_json(self):
387
+ """
388
+ Returns a json string
389
+ :return:
390
+ """
391
+ return json.dumps(self.to_dict(), default=json_serialize)
392
+
393
+ def __is_class(self, class_name):
394
+ return str(type(class_name)).startswith("<class") and hasattr(class_name, '__weakref__')
395
+
396
+ def to_dict(self):
397
+ """
398
+ Returns a Python dictionary
399
+ :return:
400
+ """
401
+ # print(inspect.currentframe().f_back.f_code.co_qualname)
402
+ data = {}
403
+
404
+ for key, value in self.__field_definitions__.items():
405
+ current_value = getattr(self, key)
406
+
407
+ if current_value is not None and not isinstance(current_value, ForeignKeyField) and value.auto_increment and self.__is_class(current_value):
408
+ if current_value.value is None:
409
+ new_id = self.__dba__.get_next_id(table_name=self.__table_name__, column_name=value.column_name)
410
+
411
+ if new_id is not None:
412
+ current_value.value = new_id
413
+ else:
414
+ current_value.value = current_value.default_value
415
+
416
+ data[key] = current_value.value
417
+ elif isinstance(value, IntegerField):
418
+ data[key] = int(current_value)
419
+ else:
420
+ data[key] = str(current_value)
421
+
422
+ return data
423
+
424
+
425
+ def __str__(self):
426
+ return self.to_json()
427
+
428
+ def __create_table__(self, table_name, execute=False):
429
+ sql = "create table " + table_name + " ("
430
+ counter = 0
431
+ for field, field_definition in self.__field_definitions__.items():
432
+ if counter > 0:
433
+ sql += ",\n"
434
+ sql += "\t" + field_definition.get_definition(self.__dba__.database_engine)
435
+ counter += 1
436
+
437
+ primary_keys = self.__get_primary_keys()
438
+ if primary_keys:
439
+ sql += ",\n"
440
+ sql += "\tprimary key (" + ",".join(primary_keys) + ")"
441
+ sql += "\n);\n"
442
+
443
+ if execute:
444
+ self.__dba__.execute(sql)
445
+ else:
446
+ return sql
447
+
448
+ def create_table(self):
449
+ """
450
+ Creates the table for the ORM structure
451
+ :return:
452
+ """
453
+ self.__dba__.create_table(self.__table_name__, True)
454
+
455
+ def __build_sql(self, column_names="*", join="", filter="", group_by="", having="", order_by=""):
456
+ """
457
+ Helper method to build the SQL query
458
+ :param column_names:
459
+ :param join:
460
+ :param filter:
461
+ :param group_by:
462
+ :param having:
463
+ :param order_by:
464
+ :return:
465
+ """
466
+ if isinstance(column_names, str):
467
+ if column_names in ("", "*"):
468
+ cols = "*"
469
+ else:
470
+ cols = ",\n".join(c.strip() for c in column_names.split(','))
471
+ else:
472
+ cols = ",\n".join(column_names)
473
+
474
+ group_by = [g.strip() for g in group_by.split(',')] if isinstance(group_by, str) and group_by else group_by if isinstance(group_by, list) else []
475
+ having = [h.strip() for h in having.split(',')] if isinstance(having, str) and having else having if isinstance(having, list) else []
476
+ order_by = [o.strip() for o in order_by.split(',')] if isinstance(order_by, str) and order_by else order_by if isinstance(order_by, list) else []
477
+
478
+ sql = f"select {cols}\nfrom {self.__table_name__} as t"
479
+ if join: sql += f"\n{join}"
480
+ if filter: sql += f"\nwhere {filter}"
481
+ if group_by: sql += "\ngroup by " + ", ".join(group_by)
482
+ if having: sql += "\nhaving " + ", ".join(having)
483
+ if order_by: sql += "\norder by " + ", ".join(order_by)
484
+ return sql
485
+
486
+ def fetch_one(self, column_names="*", filter="", params=[], join="", group_by="", having="", order_by=""):
487
+ """
488
+ Fetch one record from the database
489
+ :param column_names:
490
+ :param filter:
491
+ :param params:
492
+ :param join:
493
+ :param group_by:
494
+ :param having:
495
+ :param order_by:
496
+ :return:
497
+ """
498
+ sql = self.__build_sql(column_names, join, filter, group_by, having, order_by)
499
+ return self.__dba__.fetch_one(sql, params=params)
500
+
501
+ def fetch(self, column_names="*", filter="", params=[], join="", group_by="", having="", order_by="", limit=10, skip=0):
502
+ """
503
+ Fetch multiple records from the database
504
+ :param column_names:
505
+ :param filter:
506
+ :param params:
507
+ :param join:
508
+ :param group_by:
509
+ :param having:
510
+ :param order_by:
511
+ :param limit:
512
+ :param skip:
513
+ :return:
514
+ """
515
+ sql = self.__build_sql(column_names, join, filter, group_by, having, order_by)
516
+ return self.__dba__.fetch(sql, params=params, limit=limit, skip=skip)
517
+
518
+ def select (self, column_names="*", filter="", params=[], join="", group_by="", having="", order_by="", limit=10, skip=0):
519
+ return self.fetch(column_names, filter, params, join, group_by, having, order_by, limit, skip)
520
+
521
+ def load(self, query="", params=[]):
522
+ """
523
+ Loads a single record into the object based on the primary key or query if query is set
524
+ :param query:
525
+ :param params:
526
+ :return:
527
+ """
528
+ if not self.__table_exists:
529
+ Debug("ORM: Load Error - Table", self.__table_name__, "does not exist", TINA4_LOG_ERROR)
530
+ return False
531
+
532
+ if query == "":
533
+ sql = f"select * from {self.__table_name__} where "
534
+ primary_keys = self.__get_primary_keys()
535
+ values = self.to_dict()
536
+ counter = 0
537
+ for key in primary_keys:
538
+ if counter > 0:
539
+ sql += " and "
540
+ sql += key + " = '" + str(values[key]) + "'"
541
+ counter += 1
542
+ else:
543
+ sql = f"select * from {self.__table_name__} where {query}"
544
+
545
+ if self.__dba__ is not None:
546
+ record = self.__dba__.fetch_one(sql, params)
547
+ try:
548
+ if record:
549
+ for key, value in record.items():
550
+ if key in self.__field_definitions__:
551
+ field_value = self.__field_definitions__[key]
552
+ field_value.value = value
553
+
554
+ setattr(self, key, field_value)
555
+ return True
556
+ else:
557
+ return False
558
+ except Exception as e:
559
+ Debug("ORM Load Error", str(e), TINA4_LOG_ERROR)
560
+ else:
561
+ Debug("Database not initialized", TINA4_LOG_ERROR)
562
+ return False
563
+
564
+ def save(self):
565
+ """
566
+ Saves the ORM object to the database
567
+ :return:
568
+ """
569
+ if not self.__table_exists:
570
+ Debug("ORM: Save Error - Table", self.__table_name__, "does not exist", TINA4_LOG_ERROR)
571
+ return False
572
+ # check if record exists
573
+ data = self.to_dict()
574
+
575
+ primary_keys = self.__get_primary_keys()
576
+ sql = "select count(*) as \"count_records\" from " + self.__table_name__ + " where "
577
+ counter = 0
578
+ input_params = []
579
+ for key in primary_keys:
580
+ if counter > 0:
581
+ sql += " and "
582
+ sql += key + " = ?"
583
+ input_params.append(data[key])
584
+ counter += 1
585
+
586
+ try:
587
+ record = self.__dba__.fetch_one(sql, input_params)
588
+ for key, value in data.items():
589
+ if key in self.__field_definitions__:
590
+ if type(value) == BlobField and (isinstance(value, dict) or isinstance(value, list)) or (isinstance(value, str) and value.startswith("{") and value.endswith("}")):
591
+ Debug.warning("Saving BlobField as JSON", key)
592
+ data[key] = json.dumps(value)
593
+
594
+ if record["count_records"] == 0:
595
+ result = self.__dba__.insert(self.__table_name__, data)
596
+ else:
597
+ result = self.__dba__.update(self.__table_name__, data)
598
+
599
+ self.__dba__.commit()
600
+
601
+ if result:
602
+ self.load()
603
+
604
+ return True
605
+ except Exception as e:
606
+ Debug.error("Error saving", str(e))
607
+ return False
608
+
609
+ return result
610
+
611
+ def delete(self, query="", params=[]):
612
+ if not self.__table_exists:
613
+ Debug("ORM: Load Error - Table", self.__table_name__, "does not exist", TINA4_LOG_ERROR)
614
+ return False
615
+ if query == "":
616
+ sql = f"delete from {self.__table_name__} where "
617
+ primary_keys = self.__get_primary_keys()
618
+ values = self.to_dict()
619
+ counter = 0
620
+ for key in primary_keys:
621
+ if counter > 0:
622
+ sql += " and "
623
+ sql += key + " = '" + str(values[key]) + "'"
624
+ counter += 1
625
+ else:
626
+ sql = f"delete from {self.__table_name__} where {query}"
627
+
628
+ result = False
629
+ if self.__dba__ is not None:
630
+ result = self.__dba__.execute(sql, params)
631
+
632
+ if result.error is not None:
633
+ self.__dba__.rollback()
634
+ return False
635
+ else:
636
+ self.__dba__.commit()
637
+ return True
638
+
639
+