db-attribute 2.1.1.1__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.1
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
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
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)
@@ -181,19 +311,19 @@ If a developer needs to recreate an object, he can call DbAttribute cls with id.
181
311
 
182
312
  ```python
183
313
  obj = User(name='Ben', age=20, id=3) #insert obj to db
184
- print(obj) #User(id=3, name='Ben', age=20)
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=20)
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=20)
320
+ print(obj) # User(id=3, name='Anna', age=20)
191
321
 
192
322
  obj = User(age=25, id=3)
193
- print(obj) #User(id=3, name='Anna', age=25)
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=25)
326
+ print(obj) # User(id=3, name='Anna', age=25)
197
327
  ```
198
328
 
199
329
  ### Finding objects
@@ -211,18 +341,21 @@ obj = User(name='Bob', age=20, id=1)
211
341
  obj = User(name='Bob', age=30, id=2)
212
342
  obj = User(name='Anna', age=20, id=3)
213
343
  #finds objs
214
- print(User.get(id=2)) #User(id=2, name='Bob', age=30)
215
- print(User.get((User.age == 30) & (User.name == 'Bob')))#User(id=2, name='Bob', age=30)
216
- print(User.get(User.name == 'Anna')) #User(id=3, name='Anna', age=20)
217
- print(User.get(User.name == 'Bob')) #User(id=1, name='Bob', age=20)
218
- 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)]
219
352
  ```
220
353
 
221
354
  To check the correctness of writing a logical expression, you can:
222
355
 
223
356
  ```python
224
- print(User.name == 'Anna') #(User.name = 'Anna')
225
- print((User.age == 30) & (User.name == 'Bob')) #((User.age = 30) 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'))
226
359
  ```
227
360
 
228
361
  Use '&' and '|' instead of the 'and' and 'or' operators. The 'and' and 'or' operators are not supported
@@ -233,16 +366,16 @@ If a developer needs to iterate through all the elements of a class, they can us
233
366
 
234
367
  ```python
235
368
  print(list(User))
236
- #[User(id=1, name='Bob', age=30), User(id=2, name='Bob', age=20), User(id=3, name='Anna', age=20)]
369
+ # [User(id=1, name='Bob', age=30), User(id=2, name='Bob', age=20), User(id=3, name='Anna', age=20)]
237
370
 
238
371
  print([i for i in User if i.age < 30])
239
- #[User(id=2, name='Bob', age=20), User(id=3, name='Anna', age=20)]
372
+ # [User(id=2, name='Bob', age=20), User(id=3, name='Anna', age=20)]
240
373
 
241
374
  for i in User:
242
375
  print(i)
243
- #User(id=1, name='Bob', age=30)
244
- #User(id=2, name='Bob', age=20)
245
- #User(id=3, name='Anna', age=20)
376
+ # User(id=1, name='Bob', age=30)
377
+ # User(id=2, name='Bob', age=20)
378
+ # User(id=3, name='Anna', age=20)
246
379
  ```
247
380
  ⚠️ Iterations loads all objects - not recommended for large tables
248
381
 
@@ -251,12 +384,12 @@ for i in User:
251
384
  ```python
252
385
  obj = User(name='Bob', list_of_books=[], id=1)
253
386
 
254
- print(obj) #User(id=1, name='Bob', list_of_books=[])
387
+ print(obj) # User(id=1, name='Bob', list_of_books=[])
255
388
 
256
389
  obj.name = 'Anna'
257
390
  obj.list_of_books.append('Any name of book')
258
391
 
259
- 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'])
260
393
  ```
261
394
 
262
395
  ### Dump mode
@@ -311,6 +444,27 @@ user.set_auto_dump_mode()
311
444
 
312
445
  ## Types
313
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
+
314
468
  ### Db attribute
315
469
 
316
470
  A developer can set the Db attribute class as data type for another Db attribute class
@@ -330,20 +484,20 @@ To create an object:
330
484
  ```python
331
485
  obj_a = Class_A(id=15, name='Anna', obj_b=1)
332
486
  obj_b = Class_B(id=1, name='Bob', obj_a=15)
333
- 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, ...)))
334
488
  #or
335
489
  obj_a = Class_A(id=15, name='Anna', obj_b=obj_b)
336
- 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, ...)))
337
491
  ```
338
492
  For found obj:
339
493
  ```python
340
494
  Class_A(id=15, name='Anna', obj_b=1)
341
495
  obj = Class_B(id=1, name='Bob', obj_a=15)
342
496
  obj = Class_A.get(Class_A.obj_b == obj)
343
- print(obj) #Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))
344
- #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:
345
499
  obj = Class_A.get(Class_A.obj_b == 1)
346
- 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, ...)))
347
501
  ```
348
502
  One-to-Many relationship:
349
503
  ```python
@@ -363,29 +517,29 @@ author = Author(name="George Orwell")
363
517
  book = Book(title="1984", author=author)
364
518
  author.books.append(book)
365
519
 
366
- print(author) #Author(id=1, name='George Orwell', books=[Book(id=1, title='1984', author=Author(id=1, ...))])
367
- 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, ...)]))
368
522
  ```
369
523
 
370
524
  ### Db classes
371
525
  When collections are stored in memory, they converted to Db classes
372
526
  ```python
373
527
  obj = User(1, list_of_books=[1, 2, 3])
374
- print(type(obj.list_of_books)) #DbList
528
+ print(type(obj.list_of_books)) # DbList
375
529
  ```
376
530
  ```python
377
531
  obj = User(1, times=[datetime(2024, 1, 1)])
378
- print(type(obj.times[0])) #DbDatetime
532
+ print(type(obj.times[0])) # DbDatetime
379
533
  ```
380
534
  And when collections dumped to db, they converted to json
381
535
  ```python
382
536
  obj = User(1, list_of_books=[1, 2, 3])
383
- 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]}
384
538
  ```
385
539
  ```python
386
540
  obj = User(1, times=[datetime(2024, 1, 1), datetime(2027, 7, 7)])
387
541
  print(obj.list_of_books.dumps())
388
- #{"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"}]}
389
543
  ```
390
544
 
391
545
  ### Custom Db Classes
@@ -477,6 +631,7 @@ obj.settings = {1: 3} # changed
477
631
  print(obj.settings) #{'1': 3}
478
632
  ```
479
633
 
634
+
480
635
  # Speed Test
481
636
 
482
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
@@ -513,4 +668,3 @@ JsonType | 7297 op/sec | -14%
513
668
  # Data base
514
669
 
515
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>
516
-
@@ -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.1'
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__)