db-attribute 2.1.1.0__tar.gz → 2.1.2__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,23 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: db_attribute
3
- Version: 2.1.1.0
3
+ Version: 2.1.2
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
- License: MIT
8
+ License-Expression: MIT
9
9
  Project-URL: Homepage, https://github.com/shutkanos/Db-Attribute
10
10
  Project-URL: Documentation, https://github.com/shutkanos/Db-Attribute#readme.md
11
11
  Project-URL: Repository, https://github.com/shutkanos/Db-Attribute
12
- Keywords: db,database,attribute,db_attribute,db attribute,DbAttribute,database attribute
13
- Classifier: Development Status :: 4 - Beta
12
+ Keywords: db,database,attribute,db_attribute,db attribute,DbAttribute,database attribute,orm,ORM,attribute orm,attribute ORM,attr,attr ORM,attr orm,db attr,DbAttr
13
+ Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Intended Audience :: Developers
15
- Classifier: License :: OSI Approved :: MIT License
16
15
  Classifier: Programming Language :: Python :: 3
17
16
  Requires-Python: >=3.9
18
17
  Description-Content-Type: text/markdown
19
18
  License-File: LICENSE
20
- Requires-Dist: mysql-connector-python>=1.4
19
+ Requires-Dist: mysql-connector-python>=8.4.0
21
20
  Requires-Dist: orjson
22
21
  Dynamic: author
23
22
  Dynamic: home-page
@@ -57,8 +56,13 @@ The library provides tools for declarative model definition, relationship manage
57
56
  * [Supported types](#supported-types)
58
57
  * [Install](#install)
59
58
  * [How to use it](#how-to-use-it)
59
+ * [Connect to DB](#connect-to-db)
60
+ * [MySQL](#mysql)
61
+ * [SQLite](#sqlite)
60
62
  * [Create class](#create-class)
61
63
  * [Options](#options)
64
+ * [Register db work object](#register-db-work-object)
65
+ * [Class inheritance](#class-inheritance)
62
66
  * [Work with obj](#work-with-obj)
63
67
  * [Create new object](#create-new-object)
64
68
  * [Finding objects](#finding-objects)
@@ -66,6 +70,7 @@ The library provides tools for declarative model definition, relationship manage
66
70
  * [Change attribute of obj](#change-attribute-of-obj)
67
71
  * [Dump mode](#dump-mode)
68
72
  * [Types](#types)
73
+ * [DbField](#dbfield)
69
74
  * [Db attribute](#db-attribute)
70
75
  * [Db classes](#db-classes)
71
76
  * [Custom Db Classes](#custom-db-classes)
@@ -97,6 +102,37 @@ pip install git+https://github.com/shutkanos/Db-Attribute.git
97
102
 
98
103
  # How to use it
99
104
 
105
+ ## Connect to DB
106
+
107
+ ### MySQL
108
+ Connect to MySQL database by providing host, credentials, and database name:
109
+
110
+ ```python
111
+ from db_attribute import db_work, connector
112
+
113
+ connect_obj = connector.MySQLConnection(host='localhost', user='root', password='password', database='mydb')
114
+ db_work_obj = db_work.Db_work(connect_obj)
115
+ ```
116
+
117
+ ### SQLite
118
+ Connect to SQLite database (local file or in-memory):
119
+
120
+ ```python
121
+ from db_attribute import db_work, connector
122
+
123
+ # File-based database
124
+ connect_obj = connector.SQLiteConnection('/path/to/database.db')
125
+ db_work_obj = db_work.Db_work(connect_obj)
126
+
127
+ # In-memory database
128
+ connect_obj = connector.SQLiteConnection(':memory:')
129
+ db_work_obj = db_work.Db_work(connect_obj)
130
+
131
+ # With specific options
132
+ connect_obj = connector.SQLiteConnection('/path/to/database.db', timeout=10)
133
+ db_work_obj = db_work.Db_work(connect_obj)
134
+ ```
135
+
100
136
  ## Create class
101
137
 
102
138
  To create any class (Table):
@@ -110,16 +146,17 @@ To create any class (Table):
110
146
  from db_attribute import DbAttribute, DbAttributeMetaclass, db_work, connector
111
147
  from db_attribute.db_types import DbField
112
148
 
113
- connect_obj = connector.Connection(host=*mysqlhost*, user=*user*, password=*password*, database=*databasename*)
149
+ connect_obj = connector.MySQLConnection(host='localhost', user='root', password='password', database='mydb')
114
150
  db_work_obj = db_work.Db_work(connect_obj)
115
151
 
152
+
116
153
  class User(DbAttribute, metaclass=DbAttributeMetaclass, __dbworkobj__=db_work_obj):
117
- name: str = DbField(default='NotSet') # Ok
118
- age: int = -1 # Ok
119
- ban = DbField(default=False) # Ok
120
- other_int_information = 100 # Need annotation or DbField - not error, but not saved
121
- list_of_books = DbField(default_factory=lambda: ['name of first book']) # Ok
122
- settings: dict = DbField(default_factory=dict) # Ok
154
+ name: str = DbField(default='NotSet') # Ok
155
+ age: int = -1 # Ok
156
+ ban = DbField(default=False) # Ok
157
+ other_int_information = 100 # Need annotation or DbField - not error, but not saved
158
+ list_of_books = DbField(default_factory=lambda: ['name of first book']) # Ok
159
+ settings: dict = DbField(default_factory=dict) # Ok
123
160
  ```
124
161
 
125
162
  Each instance has a unique `id` identifier. It is inherited from DbAttribute and stored in `__dict__`
@@ -155,6 +192,96 @@ All options:
155
192
  * `__dbworkobj__` - database work object (required parameter),
156
193
  * `__max_repr_recursion_limit__` - maximum recursion limit for `__repr__` of DbAttribute
157
194
  * `__repr_class_name__` - sets the name of this class when using the method `__repr__` of DbAttribute
195
+ * `__table_name__` - sets custom table name instead of the class name. By default, it is inherited (like any other class attribute), so child classes automatically share the same tables as the parent.
196
+
197
+ ### Register db work object
198
+
199
+ If `dbworkobj` is not available at the time of class definition, you can defer registration using one of two approaches.
200
+
201
+ #### Using `__skip_dbworkobj__ = True`
202
+
203
+ Set `__skip_dbworkobj__ = True` in `Meta`, then call `register_dbworkobj()` on each class individually:
204
+
205
+ ```python
206
+ class BaseMeta:
207
+ __skip_dbworkobj__ = True
208
+
209
+ class User(DbAttribute, metaclass=DbAttributeMetaclass):
210
+ Meta = BaseMeta
211
+
212
+ class Book(DbAttribute, metaclass=DbAttributeMetaclass):
213
+ Meta = BaseMeta
214
+
215
+ User.register_dbworkobj(dbworkobj)
216
+ Book.register_dbworkobj(dbworkobj)
217
+ ```
218
+
219
+ #### Using `DbWorkMarker` (recommended for multiple classes)
220
+
221
+ Define a marker with a group name, place it in `Meta.__dbworkobj__`, then connect all classes at once via `DbWorkManager`:
222
+
223
+ ```python
224
+ from db_attribute.db_types import DbWorkMarker, DbWorkManager
225
+
226
+ class MainMeta:
227
+ __dbworkobj__ = DbWorkMarker('main')
228
+
229
+ class User(DbAttribute, metaclass=DbAttributeMetaclass):
230
+ Meta = MainMeta
231
+
232
+ class Book(DbAttribute, metaclass=DbAttributeMetaclass):
233
+ Meta = MainMeta
234
+
235
+ DbWorkManager.connect('main', dbworkobj)
236
+ ```
237
+
238
+ ### Class inheritance
239
+
240
+ A developer can create a child class from an existing DbAttribute class. The child class stores **all its fields** (including inherited ones) in its own separate database tables, fully isolated from the parent's tables.
241
+
242
+ ```python
243
+ from db_attribute.db_types import DbField
244
+
245
+ class UserBase(DbAttribute, metaclass=DbAttributeMetaclass):
246
+ Meta = BaseMeta
247
+ nameuser: str = DbField(default="")
248
+ rank: str = DbField(default="User")
249
+
250
+ def promote(self):
251
+ self.rank = 'Admin'
252
+
253
+ class UserChild(UserBase):
254
+ some_data: int = DbField(default=0)
255
+
256
+ user1 = UserChild(id=1, nameuser="Oleg", some_data=1)
257
+ user2 = UserChild(id=2, nameuser="Bob", some_data=2)
258
+ print(user1) # UserChild(id=1, nameuser='Oleg', rank='User', some_data=1)
259
+ print(user2) # UserChild(id=2, nameuser='Bob', rank='User', some_data=2)
260
+ user2.promote()
261
+ print(user2) # UserChild(id=2, nameuser='Bob', rank='Admin', some_data=2)
262
+ ```
263
+
264
+ Key points:
265
+
266
+ * The child class inherits all fields and methods from the parent
267
+ * No need to repeat `metaclass=DbAttributeMetaclass` — it is inherited automatically
268
+ * The child uses the same `dbworkobj` as the parent by default; a different one can be set via `Meta`
269
+ * `UserBase` and `UserChild` are completely independent in the database: `UserBase(id=1)` and `UserChild(id=1)` are different records stored in different tables
270
+ * Methods defined in the parent work correctly on child instances and write to the child's own tables:
271
+
272
+ ```python
273
+ user1.promote()
274
+ print(user1.rank) # 'Admin' — written to table 'cls userchild atr rank', not 'cls userbase atr rank'
275
+ ```
276
+
277
+ To use a different `dbworkobj` for the child class, set it via `Meta`:
278
+
279
+ ```python
280
+ class UserPremium(UserBase):
281
+ class Meta:
282
+ __dbworkobj__ = other_dbworkobj
283
+ premium_level: int = DbField(default=1)
284
+ ```
158
285
 
159
286
  ## Work with obj
160
287
 
@@ -165,6 +292,9 @@ To create an object, use an id (optional) and other fields (optional),
165
292
  ```python
166
293
  obj = User(id=3) # other field set to defaults value
167
294
  print(obj) # User(id=3, name=*default value*)
295
+ # or:
296
+ obj = User.get(id=3)
297
+ print(obj) # User(id=3, name=*default value*)
168
298
  ```
169
299
  ```python
170
300
  obj = User(name='Ben', id=3)
@@ -180,20 +310,20 @@ print(obj) # User(id=5, name='Alica')
180
310
  If a developer needs to recreate an object, he can call DbAttribute cls with id.
181
311
 
182
312
  ```python
183
- obj = User(name='Ben', age=10, id=3) #insert obj to db
184
- print(obj) #User(id=3, name='Ben', age=10)
313
+ obj = User(name='Ben', age=20, id=3) #insert obj to db
314
+ print(obj) # User(id=3, name='Ben', age=20)
185
315
 
186
316
  obj = User(id=3)
187
- print(obj) #User(id=3, name='Ben', age=10)
317
+ print(obj) # User(id=3, name='Ben', age=20)
188
318
 
189
319
  obj = User('Anna', id=3)
190
- print(obj) #User(id=3, name='Anna', age=10)
320
+ print(obj) # User(id=3, name='Anna', age=20)
191
321
 
192
- obj = User(age=15, id=3)
193
- print(obj) #User(id=3, name='Anna', age=15)
322
+ obj = User(age=25, id=3)
323
+ print(obj) # User(id=3, name='Anna', age=25)
194
324
 
195
325
  obj = User(id=3)
196
- print(obj) #User(id=3, name='Anna', age=15)
326
+ print(obj) # User(id=3, name='Anna', age=25)
197
327
  ```
198
328
 
199
329
  ### Finding objects
@@ -207,21 +337,25 @@ The `get()` method returns:
207
337
 
208
338
  ```python
209
339
  #create objs
210
- obj = User(name='Bob', age=2, id=1)
211
- obj = User(name='Bob', age=3, id=2)
212
- obj = User(name='Anna', age=2, id=3)
340
+ obj = User(name='Bob', age=20, id=1)
341
+ obj = User(name='Bob', age=30, id=2)
342
+ obj = User(name='Anna', age=20, id=3)
213
343
  #finds objs
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)
217
- print(User.get(User.name == 'Other name')) #None
344
+ print(User.get(id=2)) # User(id=2, name='Bob', age=30)
345
+ print(User.get((User.age == 30) & (User.name == 'Bob')))# User(id=2, name='Bob', age=30)
346
+ print(User.get(User.name == 'Anna')) # User(id=3, name='Anna', age=20)
347
+ print(User.get(User.name == 'Bob')) # User(id=1, name='Bob', age=20)
348
+ print(User.get(User.name == 'Other name')) # None
349
+ #finds all objs
350
+ print(User.gets(User.name == 'Bob')) # [User(id=1, name='Bob', age=20), User(id=2, name='Bob', age=30)]
351
+ print(User.gets([2, 3])) # [User(id=2, name='Bob', age=30), User(id=3, name='Anna', age=20)]
218
352
  ```
219
353
 
220
354
  To check the correctness of writing a logical expression, you can:
221
355
 
222
356
  ```python
223
- print(User.name == 'Anna') #(User.name = 'Anna')
224
- print((User.age == 3) & (User.name == 'Bob')) #((User.age = 3) and (User.name = 'Bob'))
357
+ print(User.name == 'Anna') # (User.name = 'Anna')
358
+ print((User.age == 30) & (User.name == 'Bob')) # ((User.age = 30) and (User.name = 'Bob'))
225
359
  ```
226
360
 
227
361
  Use '&' and '|' instead of the 'and' and 'or' operators. The 'and' and 'or' operators are not supported
@@ -232,16 +366,16 @@ If a developer needs to iterate through all the elements of a class, they can us
232
366
 
233
367
  ```python
234
368
  print(list(User))
235
- #[User(id=1, name='Bob', age=3), User(id=2, name='Bob', age=2), User(id=3, name='Anna', age=2)]
369
+ # [User(id=1, name='Bob', age=30), User(id=2, name='Bob', age=20), User(id=3, name='Anna', age=20)]
236
370
 
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)]
371
+ print([i for i in User if i.age < 30])
372
+ # [User(id=2, name='Bob', age=20), User(id=3, name='Anna', age=20)]
239
373
 
240
374
  for i in User:
241
375
  print(i)
242
- #User(id=1, name='Bob', age=3)
243
- #User(id=2, name='Bob', age=2)
244
- #User(id=3, name='Anna', age=2)
376
+ # User(id=1, name='Bob', age=30)
377
+ # User(id=2, name='Bob', age=20)
378
+ # User(id=3, name='Anna', age=20)
245
379
  ```
246
380
  ⚠️ Iterations loads all objects - not recommended for large tables
247
381
 
@@ -250,12 +384,12 @@ for i in User:
250
384
  ```python
251
385
  obj = User(name='Bob', list_of_books=[], id=1)
252
386
 
253
- print(obj) #User(id=1, name='Bob', list_of_books=[])
387
+ print(obj) # User(id=1, name='Bob', list_of_books=[])
254
388
 
255
389
  obj.name = 'Anna'
256
390
  obj.list_of_books.append('Any name of book')
257
391
 
258
- print(obj) #User(id=1, name='Anna', list_of_books=['Any name of book'])
392
+ print(obj) # User(id=1, name='Anna', list_of_books=['Any name of book'])
259
393
  ```
260
394
 
261
395
  ### Dump mode
@@ -310,6 +444,27 @@ user.set_auto_dump_mode()
310
444
 
311
445
  ## Types
312
446
 
447
+ ### DbField
448
+
449
+ Dbfield is used to configure fields, namely:
450
+
451
+ * `default` (Any): the default value of this Field (default takes precedence over the default_factory)
452
+ * `default_factory` (Any): the default factory of this Field
453
+ * `python_type` (Any): python type of data, example: str, int (python_type takes precedence over the data type specified in the annotation)
454
+ * `mysql_type` (str): mysql type of data, example: 'varchar(50)', 'bigint'
455
+ * `repr` (bool): Include field in `__repr__()` output
456
+ * `init` (bool): Include field in constructor (`__init__()`)
457
+ * `search_default` (bool): When True, applies default value during searches if record is missing in this field's table (Use this parameter if you understand what it is responsible for.)
458
+
459
+ For example:
460
+
461
+ ```python
462
+ class User(DbAttribute, metaclass=DbAttributeMetaclass):
463
+ Meta = BaseMeta
464
+ name: str = DbField(default='NotSet', mysql_type='varchar(32)')
465
+ hash: str = DbField(default_factory=lambda: str(uuid.uuid4()), mysql_type='varchar(512)', repr=False, init=False, search_default=False)
466
+ ```
467
+
313
468
  ### Db attribute
314
469
 
315
470
  A developer can set the Db attribute class as data type for another Db attribute class
@@ -329,20 +484,20 @@ To create an object:
329
484
  ```python
330
485
  obj_a = Class_A(id=15, name='Anna', obj_b=1)
331
486
  obj_b = Class_B(id=1, name='Bob', obj_a=15)
332
- print(obj_b) #Class_B(id=1, name='Bob', obj_a=Class_A(id=15, name='Anna', obj_b=Class_B(id=1, ...)))
487
+ print(obj_b) # Class_B(id=1, name='Bob', obj_a=Class_A(id=15, name='Anna', obj_b=Class_B(id=1, ...)))
333
488
  #or
334
489
  obj_a = Class_A(id=15, name='Anna', obj_b=obj_b)
335
- print(obj_a) #Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))
490
+ print(obj_a) # Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))
336
491
  ```
337
492
  For found obj:
338
493
  ```python
339
494
  Class_A(id=15, name='Anna', obj_b=1)
340
495
  obj = Class_B(id=1, name='Bob', obj_a=15)
341
496
  obj = Class_A.get(Class_A.obj_b == obj)
342
- print(obj) #Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))
343
- #And Found with use id of obj:
497
+ print(obj) # Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))
498
+ # And Found with use id of obj:
344
499
  obj = Class_A.get(Class_A.obj_b == 1)
345
- print(obj) #Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))
500
+ print(obj) # Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))
346
501
  ```
347
502
  One-to-Many relationship:
348
503
  ```python
@@ -362,29 +517,29 @@ author = Author(name="George Orwell")
362
517
  book = Book(title="1984", author=author)
363
518
  author.books.append(book)
364
519
 
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, ...)]))
520
+ print(author) # Author(id=1, name='George Orwell', books=[Book(id=1, title='1984', author=Author(id=1, ...))])
521
+ print(book) # Book(id=1, title='1984', author=Author(id=1, name='George Orwell', books=[Book(id=1, ...)]))
367
522
  ```
368
523
 
369
524
  ### Db classes
370
525
  When collections are stored in memory, they converted to Db classes
371
526
  ```python
372
527
  obj = User(1, list_of_books=[1, 2, 3])
373
- print(type(obj.list_of_books)) #DbList
528
+ print(type(obj.list_of_books)) # DbList
374
529
  ```
375
530
  ```python
376
531
  obj = User(1, times=[datetime(2024, 1, 1)])
377
- print(type(obj.times[0])) #DbDatetime
532
+ print(type(obj.times[0])) # DbDatetime
378
533
  ```
379
534
  And when collections dumped to db, they converted to json
380
535
  ```python
381
536
  obj = User(1, list_of_books=[1, 2, 3])
382
- print(obj.list_of_books.dumps()) #{"t": "DbList", "d": [1, 2, 3]}
537
+ print(obj.list_of_books.dumps()) # {"t": "DbList", "d": [1, 2, 3]}
383
538
  ```
384
539
  ```python
385
540
  obj = User(1, times=[datetime(2024, 1, 1), datetime(2027, 7, 7)])
386
541
  print(obj.list_of_books.dumps())
387
- #{"t": "DbList", "d": [{"t": "DbDatetime", "d": "2024-01-01T00:00:00"}, {"t": "DbDatetime", "d": "2027-07-07T00:00:00"}]}
542
+ # {"t": "DbList", "d": [{"t": "DbDatetime", "d": "2024-01-01T00:00:00"}, {"t": "DbDatetime", "d": "2027-07-07T00:00:00"}]}
388
543
  ```
389
544
 
390
545
  ### Custom Db Classes
@@ -476,6 +631,7 @@ obj.settings = {1: 3} # changed
476
631
  print(obj.settings) #{'1': 3}
477
632
  ```
478
633
 
634
+
479
635
  # Speed Test
480
636
 
481
637
  The execution speed may vary from computer to computer, so you need to focus on the specified number of operations per second of a regular mysql
@@ -512,4 +668,3 @@ JsonType | 7297 op/sec | -14%
512
668
  # Data base
513
669
 
514
670
  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>
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.1.0'
10
+ __version__ = '2.1.2'
11
11
 
12
12
  class DbAttributeMetaclass(type):
13
13
  dict_classes = db_types.DictClasses()
@@ -28,7 +28,8 @@ class DbAttributeMetaclass(type):
28
28
  '__dbworkobj__': db_types.NotSet,
29
29
  '__max_repr_recursion_limit__': 10,
30
30
  '__repr_class_name__': db_types.NotSet,
31
- '__skip_dbworkobj__': False
31
+ '__skip_dbworkobj__': False,
32
+ '__table_name__': db_types.NotSet
32
33
  }
33
34
 
34
35
  __annotations__ = {}
@@ -66,12 +67,24 @@ class DbAttributeMetaclass(type):
66
67
  if not params_for_metaclass['need_DbAttributeMetaclass']:
67
68
  return new_cls
68
69
 
69
- new_cls.__repr_class_name__ = name
70
-
71
70
  for i in options:
72
71
  if options[i] is not db_types.NotSet:
73
72
  setattr(new_cls, i, options[i])
74
73
 
74
+ if getattr(new_cls, '__table_name__', db_types.NotSet) is db_types.NotSet:
75
+ new_cls.__table_name__ = name
76
+
77
+ dbworkobj_value = getattr(new_cls, '__dbworkobj__', None)
78
+ if isinstance(dbworkobj_value, db_types.DbWorkMarker):
79
+ db_types.DbWorkManager.register_class(dbworkobj_value.group_id, new_cls)
80
+ new_cls.__skip_dbworkobj__ = True
81
+
82
+ if '__repr_class_name__' not in kwargs:
83
+ own_meta = namespace.get('Meta', None)
84
+ own_meta_has_repr = own_meta is not None and '__repr_class_name__' in getattr(own_meta, '__dict__', {})
85
+ if not own_meta_has_repr:
86
+ new_cls.__repr_class_name__ = name
87
+
75
88
  if (getattr(new_cls, '__dbworkobj__', None) is None) and (not getattr(new_cls, '__skip_dbworkobj__', False)):
76
89
  raise Exception(f'The "{new_cls.__name__}" class dosn\'t have "__dbworkobj__" parameter: set "__dbworkobj__" or "Meta", see documentation')
77
90
 
@@ -90,6 +103,11 @@ class DbAttributeMetaclass(type):
90
103
  if get_origin(attr_type) is ClassVar:
91
104
  continue
92
105
 
106
+ if isinstance(attr_value, discriptor.DbAttributeDiscriptor):
107
+ if attr_name in __db_fields__:
108
+ continue
109
+ attr_value = db_types.MISSING
110
+
93
111
  if isinstance(attr_value, db_types.DbField):
94
112
  db_field = attr_value
95
113
  elif isinstance(attr_value, db_types.Factory):
@@ -145,14 +163,14 @@ class DbAttributeMetaclass(type):
145
163
  " used_keys = now_locals\n"
146
164
  " for i in ['self', 'id', '_dont_add_id']:\n"
147
165
  " used_keys.pop(i)\n"
148
- " if not self.__dbworkobj__.cheak_exists_id_table(self.__class__.__name__):\n"
149
- " self.__dbworkobj__.create_id_table(self.__class__.__name__)\n"
166
+ " if not self.__dbworkobj__.cheak_exists_id_table(self.__class__.__table_name__):\n"
167
+ " self.__dbworkobj__.create_id_table(self.__class__.__table_name__)\n"
150
168
  " if isinstance(id, db_types.Id):\n"
151
169
  " id = id.Id\n"
152
170
  " if id is db_types.NotSet:\n"
153
- " id = self.__dbworkobj__.get_new_id(self.__class__.__name__)['data']\n"
171
+ " id = self.__dbworkobj__.get_new_id(self.__class__.__table_name__)['data']\n"
154
172
  " elif not (_dont_add_id and len([i for i in used_keys if used_keys[i] is not db_types.NotSet]) == 0):\n"
155
- " self.__dbworkobj__.add_id(self.__class__.__name__, id)\n"
173
+ " self.__dbworkobj__.add_id(self.__class__.__table_name__, id)\n"
156
174
  " setattr(self, 'id', id)\n"
157
175
  " for name in __db_fields__:\n"
158
176
  " value = used_keys[name]\n"
@@ -190,6 +208,7 @@ class DbAttribute:
190
208
  __max_repr_recursion_limit__: ClassVar[int] = 10
191
209
  __repr_class_name__: ClassVar[str] = db_types.NotSet
192
210
  __skip_dbworkobj__: ClassVar[bool] = False
211
+ __table_name__: ClassVar[str] = db_types.NotSet
193
212
 
194
213
  def __init__(self, *args, ID=None, **kwargs):
195
214
  raise 'Need set metaclass=DbAttributeMetaclass'
@@ -199,7 +218,7 @@ class DbAttribute:
199
218
  if now > self.__max_repr_recursion_limit__ or (self.id, self.__repr_class_name__) in Objs:
200
219
  return f'{self.__repr_class_name__}(id={self.id}, ...)'
201
220
  Objs.add((self.id, self.__repr_class_name__))
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__])})'''
221
+ 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__ if self.__db_fields__[i].repr])})'''
203
222
 
204
223
  def _db_attribute_container_update(self, key, data=None):
205
224
  """
@@ -217,7 +236,7 @@ class DbAttribute:
217
236
  @classmethod
218
237
  def _db_attribute_found_ids_by_attribute(cls, attribute_name:str, attribute_value):
219
238
  db_types.cheak_db_work_object(cls)
220
- tempdata = cls.__dbworkobj__.found_ids_by_value(class_name=cls.__name__, attribute_name=attribute_name, data=attribute_value, _cls_dbattribute=cls)
239
+ tempdata = cls.__dbworkobj__.found_ids_by_value(class_name=cls.__table_name__, attribute_name=attribute_name, data=attribute_value, _cls_dbattribute=cls)
221
240
  if tempdata['status_code'] != 200:
222
241
  return set()
223
242
  return tempdata['data']
@@ -237,22 +256,56 @@ class DbAttribute:
237
256
  if not Ids:
238
257
  return None
239
258
  id = Ids.list_ids[0]
259
+ if isinstance(id, db_types.Ids) and len(id) > 1:
260
+ id = id[0]
240
261
  if isinstance(id, db_types.Id):
241
262
  id = id.Id
242
- if isinstance(id, int) or isinstance(id, db_types.Id):
263
+ if isinstance(id, int):
243
264
  obj = cls.__new__(cls)
244
265
  obj.id = id
245
266
  return obj
246
267
  return None
247
268
 
269
+ @classmethod
270
+ def gets(cls, ids):
271
+ """
272
+ return List of objects
273
+ :type ids: List[int | db_types.Id] | discriptor.Condition | db_types.Ids
274
+ :return: List[obj]
275
+ """
276
+ if isinstance(ids, discriptor.Condition):
277
+ ids = ids.found()
278
+ if isinstance(ids, db_types.Ids):
279
+ ids = list(ids)
280
+ if not (isinstance(ids, list)) or isinstance(ids, db_types.Ids):
281
+ return []
282
+ res = []
283
+ for id in ids:
284
+ if isinstance(id, db_types.Id):
285
+ id = id.Id
286
+ if isinstance(id, int):
287
+ obj = cls.__new__(cls)
288
+ obj.id = id
289
+ res.append(obj)
290
+ return res
291
+
248
292
  @classmethod
249
293
  def get_all_ids(cls):
250
294
  db_types.cheak_db_work_object(cls)
251
- temp = cls.__dbworkobj__.get_all_ids(cls.__name__)
295
+ temp = cls.__dbworkobj__.get_all_ids(cls.__table_name__)
252
296
  if temp['status_code'] != 200:
253
297
  return db_types.Ids()
254
298
  return db_types.Ids(temp['data'])
255
299
 
300
+ def get_attribute(self, attribute_name, attribute_type=None):
301
+ cls = object.__getattribute__(self, '__class__')
302
+ db_field = object.__getattribute__(self, '__db_fields__').get(attribute_name, None)
303
+ if db_field is None:
304
+ return
305
+ if attribute_type is None:
306
+ attribute_type = db_field.python_type
307
+ return cls.__dict__[attribute_name].get(self, attribute_type=attribute_type)
308
+
256
309
  def dump(self, attributes:set[str]=None):
257
310
  """
258
311
  Use it func, if you need dump the data to db, with manual_dump_mode
@@ -307,7 +360,7 @@ class DbAttribute:
307
360
  attributes = all_attributes if attributes is None else attributes & all_attributes
308
361
  IDs = {IDs} if isinstance(IDs, int) else IDs
309
362
  dbworkobj = cls.__dbworkobj__
310
- clsname = cls.__name__
363
+ clsname = cls.__table_name__
311
364
  for ID in IDs:
312
365
  for db_attr in attributes:
313
366
  dbworkobj.del_attribute_value(class_name=clsname, attribute_name=db_attr, ID=ID)
@@ -335,5 +388,5 @@ class DbAttribute:
335
388
  def register_dbworkobj(cls, dbworkobj):
336
389
  cls.__dbworkobj__ = dbworkobj
337
390
  cls.__skip_dbworkobj__ = False
338
- if not cls.__dbworkobj__.cheak_exists_id_table(cls.__name__):
339
- cls.__dbworkobj__.create_id_table(cls.__name__)
391
+ if not cls.__dbworkobj__.cheak_exists_id_table(cls.__table_name__):
392
+ cls.__dbworkobj__.create_id_table(cls.__table_name__)