db-attribute 2.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Shutkanos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,24 @@
1
+ include *.md
2
+ include LICENSE
3
+ include requirements.txt
4
+ include pyproject.toml
5
+
6
+ recursive-include db_attribute *.py
7
+
8
+ recursive-exclude */__pycache__ *
9
+ recursive-exclude .git *
10
+ recursive-exclude .idea *
11
+ recursive-exclude .venv *
12
+ recursive-exclude build *
13
+ recursive-exclude dist *
14
+ recursive-exclude tests *
15
+
16
+ global-exclude *.log
17
+ global-exclude *.py[cod]
18
+ global-exclude *~
19
+ global-exclude .*
20
+ global-exclude *.bat
21
+ global-exclude *.cfg
22
+ prune db_attribute.egg-info
23
+ exclude PKG-INFO
24
+ exclude setup.cfg
@@ -0,0 +1,398 @@
1
+ Metadata-Version: 2.4
2
+ Name: db_attribute
3
+ Version: 2.1
4
+ Summary: DataBase attribute package
5
+ Home-page: https://github.com/shutkanos/Db-Attribute
6
+ Author: Shutkanos
7
+ Author-email: Shutkanos <Shutkanos836926@mail.ru>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/shutkanos/Db-Attribute
10
+ Project-URL: Documentation, https://github.com/shutkanos/Db-Attribute/readme.md
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
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: mysql-connector-python>=1.4
21
+ Requires-Dist: orjson
22
+ Dynamic: author
23
+ Dynamic: home-page
24
+ Dynamic: license-file
25
+
26
+ DbAttribute - Database Attribute
27
+ =========================
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.
30
+
31
+ * [Supported types](#supported-types)
32
+ * [Install](#install)
33
+ * [How it used](#how-it-used)
34
+ * [Create class](#create-class)
35
+ * [Options](#options)
36
+ * [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)
39
+ * [Iterations](#iterations)
40
+ * [Change attribute of obj](#change-attribute-of-obj)
41
+ * [Dump mode](#dump-mode)
42
+ * [Types](#types)
43
+ * [Db attribute](#db-attribute)
44
+ * [Db classes](#db-classes)
45
+ * [Json type](#json-type)
46
+ * [Speed Test](#speed-test)
47
+ * [Get attr](#get-attr)
48
+ * [Set attr](#set-attr)
49
+ * [Data base](#data-base)
50
+
51
+ # Supported types
52
+
53
+ This module supported standart types: `int`, `float`, `str`, `bool`, `None`, `tuple`, `list`, `set`, `dict`, `datetime`.
54
+
55
+ If developer needs other data types, he will need to write an adapter class.
56
+
57
+ # Install
58
+
59
+ Installation from source (requires git):
60
+
61
+ ```
62
+ $ pip install git+https://github.com/shutkanos/Db-Attribute.git
63
+ ```
64
+
65
+ # How it used
66
+
67
+ ## Create class
68
+
69
+ For create any classes (Tables):
70
+
71
+ * Set metaclass `DbAttributeMetaclass`
72
+ * Inheritance the `DbAttribute` (optional, since it inherits automatically when using a metaclass)
73
+ * Set dbworkobj for connect to database
74
+ * Create any fields / Annotations / DbFields for database
75
+
76
+ ```python
77
+ from db_attribute import DbAttribute, DbAttributeMetaclass, db_work, connector
78
+ from db_attribute.db_types import DbField
79
+
80
+ connect_obj = connector.Connection(host=*mysqlhost*, user=*user*, password=*password*, database=*databasename*)
81
+ db_work_obj = db_work.Db_work(connect_obj)
82
+
83
+ class User(DbAttribute, metaclass=DbAttributeMetaclass, __dbworkobj__=db_work_obj):
84
+ name: str = DbField(default='NotSet') # Ok
85
+ age: int = -1 # Ok
86
+ ban = DbField(default=False) # Ok
87
+ other_int_information = 100 # Need annotation or DbField - not error, but not saved
88
+ list_of_books = DbField(default_factory=lambda: ['name of first book']) # Ok
89
+ sittings: dict = DbField(default_factory=lambda: {}) # Ok
90
+ ```
91
+
92
+ Each class object has its own `id`. It is inherited from DbAttribute and stored in __dict__
93
+
94
+ ### Options
95
+
96
+ Options can be set in different ways:
97
+
98
+ ```python
99
+ class User(DbAttribute, metaclass=DbAttributeMetaclass, __dbworkobj__ = db_work_obj):
100
+ pass
101
+ ```
102
+ ```python
103
+ class User(DbAttribute, metaclass=DbAttributeMetaclass):
104
+ __dbworkobj__ = db_work_obj
105
+ ```
106
+ ```python
107
+ class User(DbAttribute, metaclass=DbAttributeMetaclass):
108
+ class Meta:
109
+ __dbworkobj__ = db_work_obj
110
+ ```
111
+ ```python
112
+ class BaseMeta:
113
+ __dbworkobj__ = dbworkobj
114
+ class User(DbAttribute, metaclass=DbAttributeMetaclass):
115
+ Meta = BaseMeta
116
+ ```
117
+
118
+ All options:
119
+
120
+ * `__dbworkobj__` - database work object (required parameter),
121
+ * `__max_repr_recursion_limit__` - maximum recursion limit for `__repr__` of DbAttribute
122
+ * `__repr_class_name__` - sets the name of this class when using the method `__repr__` of DbAttribute
123
+
124
+ ## Work with obj
125
+
126
+ ### Create new obj / add obj do db
127
+
128
+ For create obj use id (optional) and other fields (optional),
129
+
130
+ ```python
131
+ obj = User(id=3) # other field set to defaults value
132
+ print(obj) # User(id=3, name=*default value*)
133
+ ```
134
+ ```python
135
+ obj = User(name='Ben', id=3)
136
+ print(obj) # User(id=3, name='Ben')
137
+ ```
138
+ ```python
139
+ obj = User(name='Alica')
140
+ print(obj) # User(id=4, name='Alica')
141
+ obj = User(name='Alica')
142
+ print(obj) # User(id=5, name='Alica')
143
+ ```
144
+
145
+ If Developer need recreated obj, he can call DbAttribute cls with id.
146
+
147
+ ```python
148
+ obj = User(name='Ben', age=10, id=3) #insert obj to db
149
+ print(obj) #User(id=3, name='Ben', age=10)
150
+
151
+ obj = User(id=3)
152
+ print(obj) #User(id=3, name='Ben', age=10)
153
+
154
+ obj = User('Anna', id=3)
155
+ print(obj) #User(id=3, name='Anna', age=10)
156
+
157
+ obj = User(age=15, id=3)
158
+ print(obj) #User(id=3, name='Anna', age=15)
159
+
160
+ obj = User(id=3)
161
+ print(obj) #User(id=3, name='Anna', age=15)
162
+ ```
163
+
164
+ ### Found / get obj
165
+
166
+ if the developer needs to find an object, he can use the 'get' method.
167
+
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.
170
+
171
+ ```python
172
+ #create objs
173
+ obj = User(name='Bob', age=3, id=1)
174
+ obj = User(name='Bob', age=2, id=2)
175
+ obj = User(name='Anna', age=2, id=3)
176
+ #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)
180
+ print(User.get(User.name == 'Other name')) #None
181
+ ```
182
+
183
+ To check the correctness of writing a logical expression, you can:
184
+
185
+ ```python
186
+ print(User.name == 'Anna') #(User.name = Anna)
187
+ print((User.age == 3) & (User.name == 'Bob')) #((User.age = 3) and (User.name = Bob))
188
+ ```
189
+
190
+ Use '&', '|' instead of the 'and', 'or' operators. The 'and' and 'or' operators are not supported
191
+
192
+ ### Iterations
193
+
194
+ If a developer needs to iterate through all the elements of a class, they can use standard Python tools.
195
+
196
+ ```python
197
+ 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)]
199
+
200
+ print([i.name for i in User])
201
+ #['Bob', 'Bob', 'Anna']
202
+
203
+ for i in User:
204
+ 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)
208
+ ```
209
+
210
+ ### Change attribute of obj
211
+
212
+ ```python
213
+ obj = User(name='Bob', list_of_books=[], id=1)
214
+
215
+ print(obj) #User(id=1, name='Bob', list_of_books=[])
216
+
217
+ obj.name = 'Anna'
218
+ obj.list_of_books.append('Any name of book')
219
+
220
+ print(obj) #User(id=1, name='Anna', list_of_books=['Any name of book'])
221
+ ```
222
+
223
+ ### Dump mode
224
+
225
+ If in any function you will work with obj, you can activate manual_dump_mode (auto_dump_mode is default),
226
+
227
+ * `auto_dump_mode`: attributes don't save in self.__dict__, all changes automatic dump in db.
228
+ * `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
229
+
230
+ DbAttribute.set_auto_dump_mode set auto_dump_mode and call dump
231
+
232
+ DbAttribute.set_manual_dump_mode set manual_dump_mode
233
+
234
+ ```python
235
+ user = User(id=1, any_db_data1=531, any_db_data2='string')
236
+ print(user.__dict__)
237
+ # {'id': 1}
238
+ user.set_manual_dump_mode()
239
+ print(user.__dict__)
240
+ # {'id': 1, '_any_db_data1': 531, '_any_db_data2': 'string'}
241
+ ```
242
+ Or set dump mod for individual attributes
243
+
244
+ ```python
245
+ user = User(id=1, any_db_data1=531, any_db_data2='string')
246
+ print(user.__dict__)
247
+ # {'id': 1}
248
+ user.set_manual_dump_mode({'any_db_data1'})
249
+ print(user.__dict__)
250
+ # {'id': 1, '_any_db_data1': 531}
251
+ ```
252
+
253
+ ```python
254
+ user = User(id=1, list_of_books=[])
255
+ user.set_manual_dump_mode()
256
+ for i in range(10 ** 5):
257
+ user.list_of_books.append(i)
258
+ user.set_auto_dump_mode()
259
+ ```
260
+ If Developer need dump attributes to db with manual_dump_mode, you can use DbAttribute.db_attribute_dump
261
+
262
+ ```python
263
+ user = User(id=1, list_of_books=[])
264
+ user.set_manual_dump_mode()
265
+ for i in range(10 ** 4):
266
+ user.list_of_books.append(i)
267
+ user.dump() # dump the list_of_books to db
268
+ for i in range(10 ** 4):
269
+ user.list_of_books.append(i)
270
+ user.set_auto_dump_mode()
271
+ ```
272
+
273
+ ## Types
274
+
275
+ ### Db attribute
276
+
277
+ Developer can set the Db attribute class as data type for another Db attribute class
278
+
279
+ ```python
280
+ from db_attribute.db_types import TableType
281
+
282
+ class Class_A(DbAttribute, metaclass=DbAttributeMetaclass):
283
+ Meta = BaseMeta
284
+ obj_b: TableType('Class_B')
285
+
286
+ class Class_B(DbAttribute, metaclass=DbAttributeMetaclass):
287
+ Meta = BaseMeta
288
+ obj_a: Class_A
289
+ ```
290
+ For create obj:
291
+ ```python
292
+ obj_a = Class_A(id=15, name='Anna', obj_b=1)
293
+ 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, ...)))
295
+ #or
296
+ 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, ...)))
298
+ ```
299
+ For found obj:
300
+ ```python
301
+ Class_A(id=15, name='Anna', obj_b=1)
302
+ obj = Class_B(id=1, name='Bob', obj_a=15)
303
+ 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, ...)))
305
+ #And Found with use id of obj:
306
+ 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, ...)))
308
+ ```
309
+
310
+ ### Db classes
311
+ When collections are stored in memory, they converted to Db classes
312
+ ```python
313
+ obj = User(1, list_of_books=[1, 2, 3])
314
+ print(type(obj.list_of_books)) #DbList
315
+ ```
316
+ ```python
317
+ obj = User(1, times=[datetime(2024, 1, 1)])
318
+ print(type(obj.times[0])) #DbDatetime
319
+ ```
320
+ And when collections dumped to db, they converted to json
321
+ ```python
322
+ obj = User(1, list_of_books=[1, 2, 3])
323
+ print(obj.list_of_books.dumps()) #{"t": "DbList", "d": [1, 2, 3]}
324
+ ```
325
+ ```python
326
+ obj = User(1, times=[datetime(2024, 1, 1), datetime(2027, 7, 7)])
327
+ print(obj.list_of_books.dumps())
328
+ #{"t": "DbList", "d": [{"t": "DbDatetime", "d": "2024-01-01T00:00:00"}, {"t": "DbDatetime", "d": "2027-07-07T00:00:00"}]}
329
+ ```
330
+
331
+ ### Json type
332
+
333
+ Db attribute support `tuple`, `list`, `dict`, other collections, but this types slow, because uses Db classes (see [speed test](#speed-test)).
334
+
335
+ To solve this problem, use a Json convertation
336
+
337
+ ```python
338
+ from db_attribute.db_types import JsonType, DbField
339
+
340
+ class User(DbAttribute, metaclass=DbAttributeMetaclass):
341
+ Meta = BaseMeta
342
+ sittings: JsonType = DbField(default_factory=lambda: {})
343
+
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
347
+ ```
348
+
349
+ * 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`
351
+
352
+ ```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}
360
+ ```
361
+
362
+ # Speed Test
363
+
364
+ 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
365
+
366
+ * mysql `select` - 12500 op/sec
367
+ * mysql `insert` - 8500 op/sec<br>
368
+
369
+ ## Get attr
370
+
371
+ Mysql `select` - 12500 op/sec
372
+
373
+ Type | Operation/seconds | How much slower is it
374
+ ----------|-------------------|---------------------------
375
+ int | 11658 op/sec | -6%
376
+ str | 11971 op/sec | -4%
377
+ tuple | 9685 op/sec | -22%
378
+ list | 9630 op/sec | -23%
379
+ dict | 9545 op/sec | -23%
380
+ JsonType | 11937 op/sec | -4%
381
+
382
+ ## Set attr
383
+
384
+ Mysql `insert` - 8500 op/sec<br>
385
+
386
+ Type | Operation/seconds | How much slower is it
387
+ ----------|-------------------|---------------------------
388
+ int | 8056 op/sec | -5%
389
+ str | 8173 op/sec | -3%
390
+ tuple | 6284 op/sec | -26%
391
+ list | 6043 op/sec | -28%
392
+ dict | 6354 op/sec | -25%
393
+ JsonType | 7297 op/sec | -14%
394
+
395
+ # Data base
396
+
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>
398
+