db-attribute 2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- db_attribute/__init__.py +320 -0
- db_attribute/connector.py +31 -0
- db_attribute/db_class.py +942 -0
- db_attribute/db_types.py +178 -0
- db_attribute/db_work.py +365 -0
- db_attribute/discriptor.py +176 -0
- db_attribute-2.1.dist-info/METADATA +398 -0
- db_attribute-2.1.dist-info/RECORD +11 -0
- db_attribute-2.1.dist-info/WHEEL +5 -0
- db_attribute-2.1.dist-info/licenses/LICENSE +21 -0
- db_attribute-2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import db_attribute.db_types as db_types
|
|
2
|
+
import db_attribute.db_class as db_class
|
|
3
|
+
import db_attribute.db_work as db_work
|
|
4
|
+
import db_attribute.connector as connector
|
|
5
|
+
import db_attribute
|
|
6
|
+
|
|
7
|
+
class ConditionCore:
|
|
8
|
+
def __gt__(self, other):
|
|
9
|
+
return Condition(self.cls, self, other, '>')
|
|
10
|
+
def __ge__(self, other):
|
|
11
|
+
return Condition(self.cls, self, other, '>=')
|
|
12
|
+
def __lt__(self, other):
|
|
13
|
+
return Condition(self.cls, self, other, '<')
|
|
14
|
+
def __le__(self, other):
|
|
15
|
+
return Condition(self.cls, self, other, '<=')
|
|
16
|
+
def __eq__(self, other):
|
|
17
|
+
return Condition(self.cls, self, other, '=')
|
|
18
|
+
def __ne__(self, other):
|
|
19
|
+
return Condition(self.cls, self, other, '!=')
|
|
20
|
+
#def _like(self, other):
|
|
21
|
+
# return Condition(self.cls, self, other, 'Like')
|
|
22
|
+
def __add__(self, other):
|
|
23
|
+
return Condition(self.cls, self, other, '+', lambda a, b: a + b)
|
|
24
|
+
def __mul__(self, other):
|
|
25
|
+
return Condition(self.cls, self, other, '*', lambda a, b: a * b)
|
|
26
|
+
def __sub__(self, other):
|
|
27
|
+
return Condition(self.cls, self, other, '-', lambda a, b: a - b)
|
|
28
|
+
def __truediv__(self, other):
|
|
29
|
+
return Condition(self.cls, self, other, '/', lambda a, b: a / b)
|
|
30
|
+
def __mod__(self, other):
|
|
31
|
+
return Condition(self.cls, self, other, '%', lambda a, b: a % b)
|
|
32
|
+
def __lshift__(self, other):
|
|
33
|
+
return Condition(self.cls, self, other, '<<', lambda a, b: a << b)
|
|
34
|
+
def __rshift__(self, other):
|
|
35
|
+
return Condition(self.cls, self, other, '>>', lambda a, b: a >> b)
|
|
36
|
+
def __and__(self, other):
|
|
37
|
+
return Condition(self.cls, self, other, 'and', lambda a, b: a & b)
|
|
38
|
+
def __or__(self, other):
|
|
39
|
+
return Condition(self.cls, self, other, 'or', lambda a, b: a | b)
|
|
40
|
+
def __xor__(self, other):
|
|
41
|
+
return Condition(self.cls, self, other, '^', lambda a, b: a ^ b)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AttributeObj(ConditionCore):
|
|
45
|
+
def __init__(self, cls, attr):
|
|
46
|
+
self.cls = cls
|
|
47
|
+
self.attr = attr
|
|
48
|
+
|
|
49
|
+
def _get_condition_repr(self):
|
|
50
|
+
field = self.cls.__db_fields__[self.attr]
|
|
51
|
+
default = field.get_default()
|
|
52
|
+
python_type = field.python_type
|
|
53
|
+
table_name = db_work.get_table_name(self.cls.__name__, self.attr)
|
|
54
|
+
sql_name = self.cls.__dbworkobj__.connobj.sql_name
|
|
55
|
+
scr_table_name = db_work.screening(table_name, sql_name)
|
|
56
|
+
scr_data = db_work.screening('data', sql_name)
|
|
57
|
+
if default is db_types.NotSet or default is db_types.MISSING:
|
|
58
|
+
return f'{scr_table_name}.{scr_data}'
|
|
59
|
+
temp = db_work.convert_attribute_value_to_mysql_value(default, python_type)
|
|
60
|
+
if temp['status_code'] != 200:
|
|
61
|
+
raise Exception(connector.status_cod[temp['status_code']])
|
|
62
|
+
sql_default = temp["data"]
|
|
63
|
+
return f'COALESCE({scr_table_name}.{scr_data}, {sql_default})'
|
|
64
|
+
|
|
65
|
+
def __repr__(self):
|
|
66
|
+
return f'{self.cls.__name__}.{self.attr}'
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class Condition(ConditionCore):
|
|
70
|
+
def __init__(self, cls, left, right, operator:str, lambda_operator=None):
|
|
71
|
+
self.cls = cls
|
|
72
|
+
self.left = left
|
|
73
|
+
self.right = right
|
|
74
|
+
self.operator = operator
|
|
75
|
+
if lambda_operator:
|
|
76
|
+
self.lambda_operator = lambda_operator
|
|
77
|
+
|
|
78
|
+
def found(self):
|
|
79
|
+
temp = self.cls.__dbworkobj__.found_ids_by_condition(class_name=self.cls.__name__, condition=self._get_condition_repr())
|
|
80
|
+
if temp['status_code'] != 200:
|
|
81
|
+
return db_types.Ids()
|
|
82
|
+
return db_types.Ids(temp['data'])
|
|
83
|
+
|
|
84
|
+
def get_value(self, ID):
|
|
85
|
+
left = self.left.get_value(ID) if isinstance(self.left, ConditionCore) else self.left
|
|
86
|
+
right = self.right.get_value(ID) if isinstance(self.right, ConditionCore) else self.right
|
|
87
|
+
return self.lambda_operator(left, right)
|
|
88
|
+
|
|
89
|
+
def _get_condition_repr(self):
|
|
90
|
+
temp = db_work.convert_attribute_value_to_mysql_value(self.left, type(self.left))
|
|
91
|
+
left = temp['data']
|
|
92
|
+
temp = db_work.convert_attribute_value_to_mysql_value(self.right, type(self.right))
|
|
93
|
+
right = temp['data']
|
|
94
|
+
return f'({left} {self.operator} {right})'
|
|
95
|
+
|
|
96
|
+
def __repr__(self):
|
|
97
|
+
return f'({self.left} {self.operator} {self.right})'
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class DbAttributeDiscriptor:
|
|
101
|
+
def __init__(self, owner=None, name:str=None):
|
|
102
|
+
self.public_name = name
|
|
103
|
+
self.private_name = name if name is None else '_' + name
|
|
104
|
+
self.cls = owner
|
|
105
|
+
self.field = None if self.cls is None else self.cls.__db_fields__[name]
|
|
106
|
+
|
|
107
|
+
def __set_name__(self, owner, name):
|
|
108
|
+
self.public_name = name
|
|
109
|
+
self.private_name = '_' + name
|
|
110
|
+
self.cls = owner
|
|
111
|
+
self.field = self.cls.__db_fields__[name]
|
|
112
|
+
|
|
113
|
+
def __get__(self, this, objtype=None):
|
|
114
|
+
if this is None:
|
|
115
|
+
return AttributeObj(self.cls, self.public_name)
|
|
116
|
+
if self.private_name in object.__getattribute__(this, '__dict__'):
|
|
117
|
+
return object.__getattribute__(this, self.private_name)
|
|
118
|
+
return self.get_attr_from_db(this)
|
|
119
|
+
|
|
120
|
+
def __set__(self, this, value):
|
|
121
|
+
if value is db_types.NotSet:
|
|
122
|
+
return
|
|
123
|
+
if self.private_name in object.__getattribute__(this, '__dict__'):
|
|
124
|
+
object.__setattr__(this, self.private_name, value)
|
|
125
|
+
return
|
|
126
|
+
self.dump_attr_to_db(this, value)
|
|
127
|
+
|
|
128
|
+
def __delete__(self, this):
|
|
129
|
+
if self.private_name in object.__getattribute__(this, '__dict__'):
|
|
130
|
+
object.__delattr__(this, self.private_name)
|
|
131
|
+
self.del_attr_from_db(this)
|
|
132
|
+
|
|
133
|
+
def __repr__(self):
|
|
134
|
+
return f'{self.cls}.{self.public_name}'
|
|
135
|
+
|
|
136
|
+
def get_default_value(self):
|
|
137
|
+
if self.field is db_types.NotSet or (self.field.default is db_types.MISSING and self.field.default_factory is db_types.MISSING): return db_types.NotSet
|
|
138
|
+
return self.field.get_default()
|
|
139
|
+
|
|
140
|
+
def dump_attr_to_db(self, this, value, cheak_exists_value=True, update_value=False):
|
|
141
|
+
attribute_type = self.cls.__db_fields__[self.public_name].python_type
|
|
142
|
+
obj = db_class.cheaker.create_db_class(value, attribute_type=attribute_type, _obj_dbattribute=this)
|
|
143
|
+
if db_work.get_table_name(self.cls.__name__, self.public_name) not in self.cls.__dbworkobj__.tables:
|
|
144
|
+
self.cls.__dbworkobj__.create_attribute_table(class_name=self.cls.__name__, attribute_name=self.public_name, attribute_type=attribute_type)
|
|
145
|
+
cheak_exists_value = False
|
|
146
|
+
ID = object.__getattribute__(this, 'id')
|
|
147
|
+
self.cls.__dbworkobj__.add_attribute_value(class_name=self.cls.__name__, attribute_name=self.public_name, ID=ID, data=obj, attribute_type=attribute_type)
|
|
148
|
+
|
|
149
|
+
def get_attr_from_db(self, this):
|
|
150
|
+
ID = object.__getattribute__(this, 'id')
|
|
151
|
+
temp_data = object.__getattribute__(self.cls, '__dbworkobj__').get_attribute_value(class_name=self.cls.__name__, attribute_name=self.public_name, ID=ID, _obj_dbattribute=this)
|
|
152
|
+
if temp_data['status_code'] == 302: #table is not create
|
|
153
|
+
object.__getattribute__(self.cls, '__dbworkobj__').create_attribute_table(class_name=self.cls.__name__, attribute_name=self.public_name, _cls_dbattribute=self.cls)
|
|
154
|
+
temp_data['status_code'] = 304
|
|
155
|
+
if temp_data['status_code'] == 304: #attr is not found
|
|
156
|
+
value = self.get_default_value()
|
|
157
|
+
if value is db_types.NotSet:
|
|
158
|
+
raise Exception(connector.status_cod[305]['eng'])
|
|
159
|
+
self.__set__(this, value)
|
|
160
|
+
return value
|
|
161
|
+
if temp_data['status_code'] != 200:
|
|
162
|
+
return None
|
|
163
|
+
return temp_data['data']
|
|
164
|
+
|
|
165
|
+
def dump_attr(self, this, cheak_exists=True):
|
|
166
|
+
if cheak_exists and (not hasattr(this, self.private_name)):
|
|
167
|
+
return
|
|
168
|
+
self.dump_attr_to_db(this, getattr(this, self.private_name))
|
|
169
|
+
|
|
170
|
+
def del_attr_from_db(self, this):
|
|
171
|
+
ID = object.__getattribute__(this, 'id')
|
|
172
|
+
return object.__getattribute__(self.cls, '__dbworkobj__').del_attribute_value(class_name=self.cls.__name__, attribute_name=self.public_name, ID=ID)
|
|
173
|
+
|
|
174
|
+
def container_update(self, this, data=None):
|
|
175
|
+
ID = object.__getattribute__(this, 'id')
|
|
176
|
+
self.cls.__dbworkobj__.add_attribute_value(class_name=self.cls.__name__, attribute_name=self.public_name, ID=ID, data=data, _cls_dbattribute=self.cls)
|
|
@@ -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
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
db_attribute/__init__.py,sha256=w5yLqFHRRWUITQWaErMg3sx4ByI0R941Tr3NhGluhhk,14408
|
|
2
|
+
db_attribute/connector.py,sha256=Ttdjj5u4l8KJARjviF8vH8nF2Ijoqd0RjWyCevtNtu0,2291
|
|
3
|
+
db_attribute/db_class.py,sha256=mkQJ77XeMWdcnPNX73M2zg1eVjyUITGurVkRy7mtDWM,50735
|
|
4
|
+
db_attribute/db_types.py,sha256=x3gx3OEw0qqnUPYBd1eQRuljczQ5moBARpTMtmn4Cok,7186
|
|
5
|
+
db_attribute/db_work.py,sha256=fzk3RP0dU-I0Tk7KISdh6W6Gks0qu2b_7z2QI3JdTL8,19601
|
|
6
|
+
db_attribute/discriptor.py,sha256=LX8H-3vPabxUgp89By8DNYz3N_uaO3uYY0sfJOQyFD4,8306
|
|
7
|
+
db_attribute-2.1.dist-info/licenses/LICENSE,sha256=gheTd8aiLMcHyg8ZcZnpyFiXZB4liiCoDTqwta5KGP0,1095
|
|
8
|
+
db_attribute-2.1.dist-info/METADATA,sha256=WEEyEdjLfFxIWIEcFnaV9aEBRhekFA7-dOmFz4WU9IM,12851
|
|
9
|
+
db_attribute-2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
db_attribute-2.1.dist-info/top_level.txt,sha256=YLmpyp35GpOP49dt8-AGQsss3GxChU4224v7qgIcd7Y,13
|
|
11
|
+
db_attribute-2.1.dist-info/RECORD,,
|
|
@@ -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 @@
|
|
|
1
|
+
db_attribute
|