db-attribute 2.1__tar.gz → 2.1.1__tar.gz

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.
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: db_attribute
3
- Version: 2.1
3
+ Version: 2.1.1.0
4
4
  Summary: DataBase attribute package
5
5
  Home-page: https://github.com/shutkanos/Db-Attribute
6
6
  Author: Shutkanos
7
7
  Author-email: Shutkanos <Shutkanos836926@mail.ru>
8
8
  License: MIT
9
9
  Project-URL: Homepage, https://github.com/shutkanos/Db-Attribute
10
- Project-URL: Documentation, https://github.com/shutkanos/Db-Attribute/readme.md
10
+ Project-URL: Documentation, https://github.com/shutkanos/Db-Attribute#readme.md
11
11
  Project-URL: Repository, https://github.com/shutkanos/Db-Attribute
12
12
  Keywords: db,database,attribute,db_attribute,db attribute,DbAttribute,database attribute
13
13
  Classifier: Development Status :: 4 - Beta
@@ -26,22 +26,49 @@ Dynamic: license-file
26
26
  DbAttribute - Database Attribute
27
27
  =========================
28
28
 
29
- This module allows you to save attributes of objects not in RAM, but in a database. the closest analogue is <a href='https://github.com/sqlalchemy/sqlalchemy'>SQLAlchemy</a>. Unlike SQLAlchemy, this module maximizes automatism, allowing the developer to focus on other details without worrying about working with the database.
29
+ DbAttribute is an ORM library designed to simplify database interactions. Core capabilities:
30
30
 
31
+ * Automatic state synchronization
32
+ Object attribute changes are automatically tracked and persisted to the database without requiring explicit commit calls.
33
+ <br><br>
34
+ * Direct object manipulation
35
+ Supports both value assignment (obj.attr = value) and in-place modification of container types:
36
+
37
+ ```python
38
+ obj.books.append("New Book")
39
+ obj.settings["theme"] = "dark"
40
+ ```
41
+
42
+ * Expressive query syntax
43
+ Filtering uses Python operators with natural syntax:
44
+
45
+ ```python
46
+ # Find users older than 18 named John
47
+ User.get((User.age > 18) & (User.name == "John"))
48
+
49
+ # Get all users named Bob
50
+ [user for user in User if user.name == "Bob"]
51
+ ```
52
+ The library provides tools for declarative model definition, relationship management, and database operation optimization through configurable synchronization modes.
53
+
54
+ # Table of contents
55
+
56
+ * [Table of contents](#table-of-contents)
31
57
  * [Supported types](#supported-types)
32
58
  * [Install](#install)
33
- * [How it used](#how-it-used)
59
+ * [How to use it](#how-to-use-it)
34
60
  * [Create class](#create-class)
35
61
  * [Options](#options)
36
62
  * [Work with obj](#work-with-obj)
37
- * [Create new obj / add obj do db](#create-new-obj--add-obj-do-db)
38
- * [Found / get obj](#found--get-obj)
63
+ * [Create new object](#create-new-object)
64
+ * [Finding objects](#finding-objects)
39
65
  * [Iterations](#iterations)
40
66
  * [Change attribute of obj](#change-attribute-of-obj)
41
67
  * [Dump mode](#dump-mode)
42
68
  * [Types](#types)
43
69
  * [Db attribute](#db-attribute)
44
70
  * [Db classes](#db-classes)
71
+ * [Custom Db Classes](#custom-db-classes)
45
72
  * [Json type](#json-type)
46
73
  * [Speed Test](#speed-test)
47
74
  * [Get attr](#get-attr)
@@ -50,28 +77,34 @@ This module allows you to save attributes of objects not in RAM, but in a databa
50
77
 
51
78
  # Supported types
52
79
 
53
- This module supported standart types: `int`, `float`, `str`, `bool`, `None`, `tuple`, `list`, `set`, `dict`, `datetime`.
80
+ This module supports standard types: `int`, `float`, `str`, `bool`, `None`, `tuple`, `list`, `set`, `dict`, `datetime`.
54
81
 
55
- If developer needs other data types, he will need to write an adapter class.
82
+ If a developer needs other data types, they will need to write an adapter class.
56
83
 
57
84
  # Install
58
85
 
59
- Installation from source (requires git):
86
+ The package can be obtained from PyPI and installed in a single step:
60
87
 
61
88
  ```
62
- $ pip install git+https://github.com/shutkanos/Db-Attribute.git
89
+ pip install db_attribute
63
90
  ```
64
91
 
65
- # How it used
92
+ It can also be obtained from source (requires git):
93
+
94
+ ```
95
+ pip install git+https://github.com/shutkanos/Db-Attribute.git
96
+ ```
97
+
98
+ # How to use it
66
99
 
67
100
  ## Create class
68
101
 
69
- For create any classes (Tables):
102
+ To create any class (Table):
70
103
 
71
104
  * Set metaclass `DbAttributeMetaclass`
72
105
  * Inheritance the `DbAttribute` (optional, since it inherits automatically when using a metaclass)
73
106
  * Set dbworkobj for connect to database
74
- * Create any fields / Annotations / DbFields for database
107
+ * Define fields using annotations or DbField for database columns
75
108
 
76
109
  ```python
77
110
  from db_attribute import DbAttribute, DbAttributeMetaclass, db_work, connector
@@ -86,10 +119,10 @@ class User(DbAttribute, metaclass=DbAttributeMetaclass, __dbworkobj__=db_work_ob
86
119
  ban = DbField(default=False) # Ok
87
120
  other_int_information = 100 # Need annotation or DbField - not error, but not saved
88
121
  list_of_books = DbField(default_factory=lambda: ['name of first book']) # Ok
89
- sittings: dict = DbField(default_factory=lambda: {}) # Ok
122
+ settings: dict = DbField(default_factory=dict) # Ok
90
123
  ```
91
124
 
92
- Each class object has its own `id`. It is inherited from DbAttribute and stored in __dict__
125
+ Each instance has a unique `id` identifier. It is inherited from DbAttribute and stored in `__dict__`
93
126
 
94
127
  ### Options
95
128
 
@@ -111,7 +144,9 @@ class User(DbAttribute, metaclass=DbAttributeMetaclass):
111
144
  ```python
112
145
  class BaseMeta:
113
146
  __dbworkobj__ = dbworkobj
114
- class User(DbAttribute, metaclass=DbAttributeMetaclass):
147
+ class Class_A(DbAttribute, metaclass=DbAttributeMetaclass):
148
+ Meta = BaseMeta
149
+ class Class_B(DbAttribute, metaclass=DbAttributeMetaclass):
115
150
  Meta = BaseMeta
116
151
  ```
117
152
 
@@ -123,9 +158,9 @@ All options:
123
158
 
124
159
  ## Work with obj
125
160
 
126
- ### Create new obj / add obj do db
161
+ ### Create new object
127
162
 
128
- For create obj use id (optional) and other fields (optional),
163
+ To create an object, use an id (optional) and other fields (optional),
129
164
 
130
165
  ```python
131
166
  obj = User(id=3) # other field set to defaults value
@@ -142,7 +177,7 @@ obj = User(name='Alica')
142
177
  print(obj) # User(id=5, name='Alica')
143
178
  ```
144
179
 
145
- If Developer need recreated obj, he can call DbAttribute cls with id.
180
+ If a developer needs to recreate an object, he can call DbAttribute cls with id.
146
181
 
147
182
  ```python
148
183
  obj = User(name='Ben', age=10, id=3) #insert obj to db
@@ -161,33 +196,35 @@ obj = User(id=3)
161
196
  print(obj) #User(id=3, name='Anna', age=15)
162
197
  ```
163
198
 
164
- ### Found / get obj
199
+ ### Finding objects
165
200
 
166
- if the developer needs to find an object, he can use the 'get' method.
201
+ If a developer needs to find an object, they can use the 'get' method.
167
202
 
168
- if the 'get' method finds multiple search results, it selects the smallest id.
169
- if the 'get' method does not find any search results, it returns None.
203
+ The `get()` method returns:
204
+ - Single object if found
205
+ - Object with smallest ID if multiple matches exist
206
+ - `None` if no matches found
170
207
 
171
208
  ```python
172
209
  #create objs
173
- obj = User(name='Bob', age=3, id=1)
174
- obj = User(name='Bob', age=2, id=2)
210
+ obj = User(name='Bob', age=2, id=1)
211
+ obj = User(name='Bob', age=3, id=2)
175
212
  obj = User(name='Anna', age=2, id=3)
176
213
  #finds objs
177
- print(User.get((User.age == 3) & (User.name == 'Bob'))) #User(id=1, name=Bob, age=3)
178
- print(User.get(User.name == 'Anna')) #User(id=3, name=Anna, age=2)
179
- print(User.get(User.name == 'Bob')) #User(id=1, name=Bob, age=3)
214
+ print(User.get((User.age == 3) & (User.name == 'Bob'))) #User(id=2, name='Bob', age=3)
215
+ print(User.get(User.name == 'Anna')) #User(id=3, name='Anna', age=2)
216
+ print(User.get(User.name == 'Bob')) #User(id=1, name='Bob', age=2)
180
217
  print(User.get(User.name == 'Other name')) #None
181
218
  ```
182
219
 
183
220
  To check the correctness of writing a logical expression, you can:
184
221
 
185
222
  ```python
186
- print(User.name == 'Anna') #(User.name = Anna)
187
- print((User.age == 3) & (User.name == 'Bob')) #((User.age = 3) and (User.name = Bob))
223
+ print(User.name == 'Anna') #(User.name = 'Anna')
224
+ print((User.age == 3) & (User.name == 'Bob')) #((User.age = 3) and (User.name = 'Bob'))
188
225
  ```
189
226
 
190
- Use '&', '|' instead of the 'and', 'or' operators. The 'and' and 'or' operators are not supported
227
+ Use '&' and '|' instead of the 'and' and 'or' operators. The 'and' and 'or' operators are not supported
191
228
 
192
229
  ### Iterations
193
230
 
@@ -195,17 +232,18 @@ If a developer needs to iterate through all the elements of a class, they can us
195
232
 
196
233
  ```python
197
234
  print(list(User))
198
- #[User(id=1, name=Bob, age=3), User(id=2, name=Bob, age=2), User(id=3, name=Anna, age=2)]
235
+ #[User(id=1, name='Bob', age=3), User(id=2, name='Bob', age=2), User(id=3, name='Anna', age=2)]
199
236
 
200
- print([i.name for i in User])
201
- #['Bob', 'Bob', 'Anna']
237
+ print([i for i in User if i.age < 3])
238
+ #[User(id=2, name='Bob', age=2), User(id=3, name='Anna', age=2)]
202
239
 
203
240
  for i in User:
204
241
  print(i)
205
- #User(id=1, name=Bob, age=3)
206
- #User(id=2, name=Bob, age=2)
207
- #User(id=3, name=Anna, age=2)
242
+ #User(id=1, name='Bob', age=3)
243
+ #User(id=2, name='Bob', age=2)
244
+ #User(id=3, name='Anna', age=2)
208
245
  ```
246
+ ⚠️ Iterations loads all objects - not recommended for large tables
209
247
 
210
248
  ### Change attribute of obj
211
249
 
@@ -222,7 +260,7 @@ print(obj) #User(id=1, name='Anna', list_of_books=['Any name of book'])
222
260
 
223
261
  ### Dump mode
224
262
 
225
- If in any function you will work with obj, you can activate manual_dump_mode (auto_dump_mode is default),
263
+ If in any function you will work with obj, you can activate manual_dump_mode (auto_dump_mode is the default),
226
264
 
227
265
  * `auto_dump_mode`: attributes don't save in self.__dict__, all changes automatic dump in db.
228
266
  * `manual_dump_mode`: attributes save in self.__dict__, and won't dump in db until self.db_attribute_set_dump_mode is called. this helps to quickly perform operations on containers db attributes
@@ -239,7 +277,7 @@ user.set_manual_dump_mode()
239
277
  print(user.__dict__)
240
278
  # {'id': 1, '_any_db_data1': 531, '_any_db_data2': 'string'}
241
279
  ```
242
- Or set dump mod for individual attributes
280
+ Or set dump mode for individual attributes
243
281
 
244
282
  ```python
245
283
  user = User(id=1, any_db_data1=531, any_db_data2='string')
@@ -257,7 +295,7 @@ for i in range(10 ** 5):
257
295
  user.list_of_books.append(i)
258
296
  user.set_auto_dump_mode()
259
297
  ```
260
- If Developer need dump attributes to db with manual_dump_mode, you can use DbAttribute.db_attribute_dump
298
+ If a developer needs to dump attributes to db with manual_dump_mode, you can use DbAttribute.db_attribute_dump
261
299
 
262
300
  ```python
263
301
  user = User(id=1, list_of_books=[])
@@ -274,7 +312,7 @@ user.set_auto_dump_mode()
274
312
 
275
313
  ### Db attribute
276
314
 
277
- Developer can set the Db attribute class as data type for another Db attribute class
315
+ A developer can set the Db attribute class as data type for another Db attribute class
278
316
 
279
317
  ```python
280
318
  from db_attribute.db_types import TableType
@@ -287,24 +325,45 @@ class Class_B(DbAttribute, metaclass=DbAttributeMetaclass):
287
325
  Meta = BaseMeta
288
326
  obj_a: Class_A
289
327
  ```
290
- For create obj:
328
+ To create an object:
291
329
  ```python
292
330
  obj_a = Class_A(id=15, name='Anna', obj_b=1)
293
331
  obj_b = Class_B(id=1, name='Bob', obj_a=15)
294
- print(obj_b) #Class_B(id=1, name=Bob, obj_a=Class_A(id=15, name=Anna, obj_b=Class_B(id=1, ...)))
332
+ print(obj_b) #Class_B(id=1, name='Bob', obj_a=Class_A(id=15, name='Anna', obj_b=Class_B(id=1, ...)))
295
333
  #or
296
334
  obj_a = Class_A(id=15, name='Anna', obj_b=obj_b)
297
- print(obj_a) #Class_A(id=15, name=Anna, obj_b=Class_B(id=1, name=Bob, obj_a=Class_A(id=15, ...)))
335
+ print(obj_a) #Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))
298
336
  ```
299
337
  For found obj:
300
338
  ```python
301
339
  Class_A(id=15, name='Anna', obj_b=1)
302
340
  obj = Class_B(id=1, name='Bob', obj_a=15)
303
341
  obj = Class_A.get(Class_A.obj_b == obj)
304
- print(obj) #Class_A(id=15, name=Anna, obj_b=Class_B(id=1, name=Bob, obj_a=Class_A(id=15, ...)))
342
+ print(obj) #Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))
305
343
  #And Found with use id of obj:
306
344
  obj = Class_A.get(Class_A.obj_b == 1)
307
- print(obj) #Class_A(id=15, name=Anna, obj_b=Class_B(id=1, name=Bob, obj_a=Class_A(id=15, ...)))
345
+ print(obj) #Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))
346
+ ```
347
+ One-to-Many relationship:
348
+ ```python
349
+ from db_attribute.db_types import DbField
350
+
351
+ class Author(DbAttribute, metaclass=DbAttributeMetaclass):
352
+ Meta = BaseMeta
353
+ name: str = ""
354
+ books: list = DbField(default_factory=list)
355
+
356
+ class Book(DbAttribute, metaclass=DbAttributeMetaclass):
357
+ Meta = BaseMeta
358
+ title: str = ""
359
+ author: Author
360
+
361
+ author = Author(name="George Orwell")
362
+ book = Book(title="1984", author=author)
363
+ author.books.append(book)
364
+
365
+ print(author) #Author(id=1, name='George Orwell', books=[Book(id=1, title='1984', author=Author(id=1, ...))])
366
+ print(book) #Book(id=1, title='1984', author=Author(id=1, name='George Orwell', books=[Book(id=1, ...)]))
308
367
  ```
309
368
 
310
369
  ### Db classes
@@ -328,9 +387,67 @@ print(obj.list_of_books.dumps())
328
387
  #{"t": "DbList", "d": [{"t": "DbDatetime", "d": "2024-01-01T00:00:00"}, {"t": "DbDatetime", "d": "2027-07-07T00:00:00"}]}
329
388
  ```
330
389
 
390
+ ### Custom Db Classes
391
+
392
+ And to create a custom 'Db class', you need to
393
+ * Create regular class
394
+ * Inherit from DbClass (DbClass - first. It is important) and your regular class for custom Db class
395
+ * Set a Decorator with or without the necessary parameters
396
+ * Set at least the `__convert_to_db__` module, according to the documentation
397
+ * add additional modules.
398
+
399
+ ```python
400
+ from db_attribute import db_class
401
+
402
+ # for exemple you have your class:
403
+
404
+ class UserDataClass:
405
+ def __init__(self, value = None):
406
+ self.value = value
407
+ def __repr__(self):
408
+ return f'UserDataClass(value={self.value})'
409
+
410
+ @db_class.DbClassDecorator
411
+ class DbUserDataClass(db_class.DbClass, UserDataClass):
412
+ def __init__(self, value=None, **kwargs):
413
+ # This is not a mandatory method
414
+ super().__init__(_call_init=False, **kwargs) # But this call is mandatory
415
+ self.__dict__['value'] = value
416
+ # Here we set the value of a variable using __dict__.
417
+ # This is not necessary, but it speeds up the work with the class.
418
+
419
+ @classmethod
420
+ def __convert_to_db__(cls, obj: UserDataClass, **kwargs):
421
+ """Methode for convert obj to dbclass - need @classmethod and kwargs"""
422
+ # This is a mandatory method
423
+ # Call with _user_db=True
424
+ # Example:
425
+ # print(type(DbUserDataClass(value=10))) #UserDataClass
426
+ # print(type(DbUserDataClass(value=10, _use_db=True))) #DbUserDataClass
427
+ return cls(_use_db=True, value=obj.value, **kwargs)
428
+
429
+ def __convert_from_db__(self):
430
+ """Reverse convert"""
431
+ # This is not a mandatory method.
432
+ return self._standart_class(value=self.value)
433
+ ```
434
+
435
+ For example:
436
+
437
+ ```python
438
+ class User(DbAttribute, metaclass=DbAttributeMetaclass):
439
+ Meta = BaseMeta
440
+ data: UserDataClass
441
+
442
+ user = User(id=1, data=UserDataClass(10))
443
+ print(user.data) # UserDataClass(value=10)
444
+ user.data.value = 5
445
+ print(user.data) # UserDataClass(value=5)
446
+ ```
447
+
331
448
  ### Json type
332
449
 
333
- Db attribute support `tuple`, `list`, `dict`, other collections, but this types slow, because uses Db classes (see [speed test](#speed-test)).
450
+ DbAttribute supports `tuple`, `list`, `dict`, other collections, but these types are slow, because uses Db classes (see [speed test](#speed-test)).
334
451
 
335
452
  To solve this problem, use a Json convertation
336
453
 
@@ -339,24 +456,24 @@ from db_attribute.db_types import JsonType, DbField
339
456
 
340
457
  class User(DbAttribute, metaclass=DbAttributeMetaclass):
341
458
  Meta = BaseMeta
342
- sittings: JsonType = DbField(default_factory=lambda: {})
459
+ settings: JsonType = DbField(default_factory=lambda: {})
343
460
 
344
- obj = User(1, sittings={1: 2, 3: [4, 5]})
345
- print(obj.sittings) # {'1': 2, '3': [4, 5]}
346
- print(type(obj.sittings)) # dict
461
+ obj = User(1, settings={1: 2, 3: [4, 5]})
462
+ print(obj.settings) # {'1': 2, '3': [4, 5]}
463
+ print(type(obj.settings)) # dict
347
464
  ```
348
465
 
349
466
  * If Developer change obj with JsonType, this obj don't dump to db, you need set the new obj
350
- * The json support only `dict`, `list`, `str`, `int`, `float`, `True`, `False`, `None`
467
+ * JsonType only supports: `dict`, `list`, `str`, `int`, `float`, `bool`, `None`
351
468
 
352
469
  ```python
353
- obj = User(1, sittings={1: 2, 3: [4, 5]})
354
- del obj.sittings['3'] # not changed
355
- obj.sittings['1'] = 3 # not changed
356
- obj.sittings |= {4: 5} # not changed
357
- print(obj.sittings) #{'1': 2, '3': [4, 5]}
358
- obj.sittings = {1: 3} # changed
359
- print(obj.sittings) #{'1': 3}
470
+ obj = User(1, settings={1: 2, 3: [4, 5]})
471
+ del obj.settings['3'] # not changed
472
+ obj.settings['1'] = 3 # not changed
473
+ obj.settings |= {4: 5} # not changed
474
+ print(obj.settings) #{'1': 2, '3': [4, 5]}
475
+ obj.settings = {1: 3} # changed
476
+ print(obj.settings) #{'1': 3}
360
477
  ```
361
478
 
362
479
  # Speed Test
@@ -370,7 +487,7 @@ The execution speed may vary from computer to computer, so you need to focus on
370
487
 
371
488
  Mysql `select` - 12500 op/sec
372
489
 
373
- Type | Operation/seconds | How much slower is it
490
+ Type | Operation/seconds | Performance impact
374
491
  ----------|-------------------|---------------------------
375
492
  int | 11658 op/sec | -6%
376
493
  str | 11971 op/sec | -4%
@@ -383,7 +500,7 @@ JsonType | 11937 op/sec | -4%
383
500
 
384
501
  Mysql `insert` - 8500 op/sec<br>
385
502
 
386
- Type | Operation/seconds | How much slower is it
503
+ Type | Operation/seconds | Performance impact
387
504
  ----------|-------------------|---------------------------
388
505
  int | 8056 op/sec | -5%
389
506
  str | 8173 op/sec | -3%
@@ -394,5 +511,5 @@ JsonType | 7297 op/sec | -14%
394
511
 
395
512
  # Data base
396
513
 
397
- this module used MySQL db (<a href="https://github.com/mysql/mysql-connector-python/blob/trunk/LICENSE.txt">Licanse</a>), and for use it, you need install <a href='https://www.mysql.com'>mysql</a>
514
+ This module uses MySQL db (<a href="https://github.com/mysql/mysql-connector-python/blob/trunk/LICENSE.txt">License</a>), and for use it, you need install <a href='https://www.mysql.com'>mysql</a>
398
515
 
@@ -7,7 +7,7 @@ import db_attribute.connector as connector
7
7
  import db_attribute.discriptor as discriptor
8
8
 
9
9
  __all__ = ['DbAttribute', 'DbAttributeMetaclass', 'db_work', 'db_class', 'discriptor', 'connector', 'db_types']
10
- __version__ = '2.1'
10
+ __version__ = '2.1.1.0'
11
11
 
12
12
  class DbAttributeMetaclass(type):
13
13
  dict_classes = db_types.DictClasses()
@@ -21,10 +21,15 @@ class DbAttributeMetaclass(type):
21
21
  if not cheak_class_in_bases(bases, DbAttribute):
22
22
  bases = (DbAttribute,) + bases
23
23
 
24
- new_cls = super().__new__(cls, name, bases, namespace)
24
+ new_cls: DbAttribute = super().__new__(cls, name, bases, namespace)
25
25
 
26
26
  params_for_metaclass = {'need_add_this_class_to_dict_classes': True, 'need_DbAttributeMetaclass': True}
27
- options = {'__dbworkobj__': db_types.NotSet, '__max_repr_recursion_limit__': 10, '__repr_class_name__': db_types.NotSet}
27
+ options = {
28
+ '__dbworkobj__': db_types.NotSet,
29
+ '__max_repr_recursion_limit__': 10,
30
+ '__repr_class_name__': db_types.NotSet,
31
+ '__skip_dbworkobj__': False
32
+ }
28
33
 
29
34
  __annotations__ = {}
30
35
  __dict__ = {}
@@ -67,7 +72,7 @@ class DbAttributeMetaclass(type):
67
72
  if options[i] is not db_types.NotSet:
68
73
  setattr(new_cls, i, options[i])
69
74
 
70
- if getattr(new_cls, '__dbworkobj__', None) is None:
75
+ if (getattr(new_cls, '__dbworkobj__', None) is None) and (not getattr(new_cls, '__skip_dbworkobj__', False)):
71
76
  raise Exception(f'The "{new_cls.__name__}" class dosn\'t have "__dbworkobj__" parameter: set "__dbworkobj__" or "Meta", see documentation')
72
77
 
73
78
  attr_names = list(__annotations__.keys())
@@ -87,6 +92,8 @@ class DbAttributeMetaclass(type):
87
92
 
88
93
  if isinstance(attr_value, db_types.DbField):
89
94
  db_field = attr_value
95
+ elif isinstance(attr_value, db_types.Factory):
96
+ db_field = db_types.DbField(default_factory=attr_value)
90
97
  else:
91
98
  db_field = db_types.DbField(default=attr_value)
92
99
 
@@ -95,7 +102,7 @@ class DbAttributeMetaclass(type):
95
102
  if db_field.python_type is db_types.MISSING:
96
103
  if db_field.default is not db_types.MISSING:
97
104
  db_field.python_type = type(db_field.default)
98
- elif db_field.default_factory is not db_types.MISSING:
105
+ elif db_field.default_factory is not db_types.MISSING: #the idea: is to add a parameter for metaclass so as not to call default_factory to determine the type of the variable.
99
106
  db_field.python_type = type(db_field.default_factory.get_value())
100
107
  if db_field.python_type is db_types.MISSING:
101
108
  raise f'the type for {attr_name} of {name} is not set (add python_type for DbField or set type in annotations or set default for DbField or set default_factory for DbField)'
@@ -132,7 +139,8 @@ class DbAttributeMetaclass(type):
132
139
  params_str = ', '.join(params)
133
140
 
134
141
  init_code = (
135
- f"def __init__(self, {params_str}, id:int=db_types.NotSet, _dont_add_id:bool = False):\n"
142
+ f"def __init__(self, {params_str + ', ' if params_str else ''}id:int=db_types.NotSet, _dont_add_id:bool = False):\n"
143
+ " db_types.cheak_db_work_object(self.__class__)\n"
136
144
  " now_locals = locals()\n"
137
145
  " used_keys = now_locals\n"
138
146
  " for i in ['self', 'id', '_dont_add_id']:\n"
@@ -167,8 +175,8 @@ class DbAttributeMetaclass(type):
167
175
  if params_for_metaclass['need_add_this_class_to_dict_classes']:
168
176
  cls.dict_classes.add(new_cls)
169
177
 
170
- if not new_cls.__dbworkobj__.cheak_exists_id_table(new_cls.__name__):
171
- new_cls.__dbworkobj__.create_id_table(new_cls.__name__)
178
+ if not new_cls.__skip_dbworkobj__:
179
+ new_cls.register_dbworkobj(new_cls.__dbworkobj__)
172
180
 
173
181
  return new_cls
174
182
 
@@ -181,6 +189,7 @@ class DbAttribute:
181
189
  __dbworkobj__: ClassVar[db_work.Db_work] = None
182
190
  __max_repr_recursion_limit__: ClassVar[int] = 10
183
191
  __repr_class_name__: ClassVar[str] = db_types.NotSet
192
+ __skip_dbworkobj__: ClassVar[bool] = False
184
193
 
185
194
  def __init__(self, *args, ID=None, **kwargs):
186
195
  raise 'Need set metaclass=DbAttributeMetaclass'
@@ -190,7 +199,7 @@ class DbAttribute:
190
199
  if now > self.__max_repr_recursion_limit__ or (self.id, self.__repr_class_name__) in Objs:
191
200
  return f'{self.__repr_class_name__}(id={self.id}, ...)'
192
201
  Objs.add((self.id, self.__repr_class_name__))
193
- return f'{self.__repr_class_name__}(id={self.id}, {", ".join([f"{i}={obj.__get_repr__(Objs, now+1) if hasattr(obj:=getattr(self, i), '__get_repr__') else f'{getattr(self, i)}'}" for i in self.__db_fields__])})'
202
+ return f'''{self.__repr_class_name__}(id={self.id}, {", ".join([f"{i}={obj.__get_repr__(Objs, now+1) if hasattr(obj:=getattr(self, i), '__get_repr__') else f'{repr(getattr(self, i))}'}" for i in self.__db_fields__])})'''
194
203
 
195
204
  def _db_attribute_container_update(self, key, data=None):
196
205
  """
@@ -207,6 +216,7 @@ class DbAttribute:
207
216
 
208
217
  @classmethod
209
218
  def _db_attribute_found_ids_by_attribute(cls, attribute_name:str, attribute_value):
219
+ db_types.cheak_db_work_object(cls)
210
220
  tempdata = cls.__dbworkobj__.found_ids_by_value(class_name=cls.__name__, attribute_name=attribute_name, data=attribute_value, _cls_dbattribute=cls)
211
221
  if tempdata['status_code'] != 200:
212
222
  return set()
@@ -237,6 +247,7 @@ class DbAttribute:
237
247
 
238
248
  @classmethod
239
249
  def get_all_ids(cls):
250
+ db_types.cheak_db_work_object(cls)
240
251
  temp = cls.__dbworkobj__.get_all_ids(cls.__name__)
241
252
  if temp['status_code'] != 200:
242
253
  return db_types.Ids()
@@ -291,6 +302,7 @@ class DbAttribute:
291
302
 
292
303
  @classmethod
293
304
  def delete_objs(cls, IDs: set[int] | int, attributes:set[str]=None):
305
+ db_types.cheak_db_work_object(cls)
294
306
  all_attributes = object.__getattribute__(cls, '__db_fields__')
295
307
  attributes = all_attributes if attributes is None else attributes & all_attributes
296
308
  IDs = {IDs} if isinstance(IDs, int) else IDs
@@ -318,3 +330,10 @@ class DbAttribute:
318
330
  for key in kwargs:
319
331
  res &= cls._db_attribute_found_ids_by_attribute(attribute_name=key, attribute_value=kwargs[key])
320
332
  return res
333
+
334
+ @classmethod
335
+ def register_dbworkobj(cls, dbworkobj):
336
+ cls.__dbworkobj__ = dbworkobj
337
+ cls.__skip_dbworkobj__ = False
338
+ if not cls.__dbworkobj__.cheak_exists_id_table(cls.__name__):
339
+ cls.__dbworkobj__.create_id_table(cls.__name__)