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.
@@ -0,0 +1,320 @@
1
+ from typing import ClassVar, get_origin
2
+
3
+ import db_attribute.db_class as db_class
4
+ import db_attribute.db_work as db_work
5
+ import db_attribute.db_types as db_types
6
+ import db_attribute.connector as connector
7
+ import db_attribute.discriptor as discriptor
8
+
9
+ __all__ = ['DbAttribute', 'DbAttributeMetaclass', 'db_work', 'db_class', 'discriptor', 'connector', 'db_types']
10
+ __version__ = '2.1'
11
+
12
+ class DbAttributeMetaclass(type):
13
+ dict_classes = db_types.DictClasses()
14
+ def __new__(cls, name, bases, namespace, **kwargs):
15
+ def cheak_class_in_bases(bases, Class):
16
+ for i in bases:
17
+ if Class in i.__mro__:
18
+ return True
19
+ return False
20
+
21
+ if not cheak_class_in_bases(bases, DbAttribute):
22
+ bases = (DbAttribute,) + bases
23
+
24
+ new_cls = super().__new__(cls, name, bases, namespace)
25
+
26
+ params_for_metaclass = {'need_add_this_class_to_dict_classes': True, 'need_DbAttributeMetaclass': True}
27
+ options = {'__dbworkobj__': db_types.NotSet, '__max_repr_recursion_limit__': 10, '__repr_class_name__': db_types.NotSet}
28
+
29
+ __annotations__ = {}
30
+ __dict__ = {}
31
+ __db_fields__ = {}
32
+ __meta_options__ = {}
33
+
34
+ for i in new_cls.__mro__[::-1]:
35
+ __db_fields__ |= getattr(i, '__db_fields__', {})
36
+ __dict__ |= getattr(i, '__dict__', {})
37
+ __annotations__ |= getattr(i, '__annotations__', {})
38
+ Meta = getattr(i, 'Meta', None)
39
+ if Meta is not None:
40
+ for i in Meta.__mro__[::-1]:
41
+ __meta_options__ |= getattr(i, '__dict__', {})
42
+
43
+ for i in __dict__:
44
+ if i in options:
45
+ options[i] = __dict__[i]
46
+ if i in params_for_metaclass:
47
+ params_for_metaclass[i] = kwargs[i]
48
+
49
+ for i in __meta_options__:
50
+ if i in options:
51
+ options[i] = __meta_options__[i]
52
+ if i in params_for_metaclass:
53
+ params_for_metaclass[i] = kwargs[i]
54
+
55
+ for i in kwargs:
56
+ if i in options:
57
+ options[i] = kwargs[i]
58
+ if i in params_for_metaclass:
59
+ params_for_metaclass[i] = kwargs[i]
60
+
61
+ if not params_for_metaclass['need_DbAttributeMetaclass']:
62
+ return new_cls
63
+
64
+ new_cls.__repr_class_name__ = name
65
+
66
+ for i in options:
67
+ if options[i] is not db_types.NotSet:
68
+ setattr(new_cls, i, options[i])
69
+
70
+ if getattr(new_cls, '__dbworkobj__', None) is None:
71
+ raise Exception(f'The "{new_cls.__name__}" class dosn\'t have "__dbworkobj__" parameter: set "__dbworkobj__" or "Meta", see documentation')
72
+
73
+ attr_names = list(__annotations__.keys())
74
+ set_attr_names = set(attr_names)
75
+ for i in __dict__:
76
+ if isinstance(__dict__[i], db_types.DbField) and i not in set_attr_names:
77
+ attr_names.append(i)
78
+ set_attr_names.add(i)
79
+
80
+ for attr_name in attr_names:
81
+ if attr_name == 'id': continue
82
+ attr_value = __dict__.get(attr_name, db_types.MISSING)
83
+ attr_type = __annotations__.get(attr_name, db_types.MISSING)
84
+
85
+ if get_origin(attr_type) is ClassVar:
86
+ continue
87
+
88
+ if isinstance(attr_value, db_types.DbField):
89
+ db_field = attr_value
90
+ else:
91
+ db_field = db_types.DbField(default=attr_value)
92
+
93
+ if attr_type is not db_types.MISSING and db_field.python_type is db_types.MISSING:
94
+ db_field.python_type = attr_type
95
+ if db_field.python_type is db_types.MISSING:
96
+ if db_field.default is not db_types.MISSING:
97
+ db_field.python_type = type(db_field.default)
98
+ elif db_field.default_factory is not db_types.MISSING:
99
+ db_field.python_type = type(db_field.default_factory.get_value())
100
+ if db_field.python_type is db_types.MISSING:
101
+ raise f'the type for {attr_name} of {name} is not set (add python_type for DbField or set type in annotations or set default for DbField or set default_factory for DbField)'
102
+
103
+ __db_fields__[attr_name] = db_field
104
+
105
+ __cls_dict__ = getattr(new_cls, '__dict__')
106
+ setattr(new_cls, '__db_fields__', __db_fields__)
107
+
108
+ for attr_name in __cls_dict__['__db_fields__']:
109
+ setattr(new_cls, attr_name, discriptor.DbAttributeDiscriptor(new_cls, attr_name))
110
+
111
+ cls.dict_classes.replace(new_cls)
112
+
113
+ required_params = []
114
+ optional_params = []
115
+
116
+ for field_name, field_value in __db_fields__.items():
117
+ is_required = False
118
+ if isinstance(field_value, db_types.DbField):
119
+ default = field_value.get_default()
120
+ if default is db_types.MISSING:
121
+ is_required = True
122
+ else:
123
+ if field_value is db_types.MISSING:
124
+ is_required = True
125
+
126
+ if is_required:
127
+ required_params.append(field_name)
128
+ else:
129
+ optional_params.append(f"{field_name}=db_types.NotSet")
130
+
131
+ params = required_params + optional_params
132
+ params_str = ', '.join(params)
133
+
134
+ init_code = (
135
+ f"def __init__(self, {params_str}, id:int=db_types.NotSet, _dont_add_id:bool = False):\n"
136
+ " now_locals = locals()\n"
137
+ " used_keys = now_locals\n"
138
+ " for i in ['self', 'id', '_dont_add_id']:\n"
139
+ " used_keys.pop(i)\n"
140
+ " if not self.__dbworkobj__.cheak_exists_id_table(self.__class__.__name__):\n"
141
+ " self.__dbworkobj__.create_id_table(self.__class__.__name__)\n"
142
+ " if isinstance(id, db_types.Id):\n"
143
+ " id = id.Id\n"
144
+ " if id is db_types.NotSet:\n"
145
+ " id = self.__dbworkobj__.get_new_id(self.__class__.__name__)['data']\n"
146
+ " elif not (_dont_add_id and len([i for i in used_keys if used_keys[i] is not db_types.NotSet]) == 0):\n"
147
+ " self.__dbworkobj__.add_id(self.__class__.__name__, id)\n"
148
+ " setattr(self, 'id', id)\n"
149
+ " for name in __db_fields__:\n"
150
+ " value = used_keys[name]\n"
151
+ " if value is db_types.NotSet:\n"
152
+ " continue\n"
153
+ " if isinstance(value, db_types.Factory):\n"
154
+ " setattr(self, name, value.get_value())\n"
155
+ " else:\n"
156
+ " setattr(self, name, value)\n"
157
+ )
158
+
159
+ exec_globals = {
160
+ 'db_types': db_types,
161
+ '__db_fields__': __db_fields__
162
+ }
163
+ exec(init_code, exec_globals)
164
+ init_method = exec_globals['__init__']
165
+
166
+ new_cls.__init__ = init_method
167
+ if params_for_metaclass['need_add_this_class_to_dict_classes']:
168
+ cls.dict_classes.add(new_cls)
169
+
170
+ if not new_cls.__dbworkobj__.cheak_exists_id_table(new_cls.__name__):
171
+ new_cls.__dbworkobj__.create_id_table(new_cls.__name__)
172
+
173
+ return new_cls
174
+
175
+ def __iter__(self):
176
+ return (self.get(id=i) for i in self.get_all_ids())
177
+
178
+
179
+ class DbAttribute:
180
+ id: int
181
+ __dbworkobj__: ClassVar[db_work.Db_work] = None
182
+ __max_repr_recursion_limit__: ClassVar[int] = 10
183
+ __repr_class_name__: ClassVar[str] = db_types.NotSet
184
+
185
+ def __init__(self, *args, ID=None, **kwargs):
186
+ raise 'Need set metaclass=DbAttributeMetaclass'
187
+ def __repr__(self):
188
+ return self.__get_repr__(set())
189
+ def __get_repr__(self, Objs: set, now: int=0):
190
+ if now > self.__max_repr_recursion_limit__ or (self.id, self.__repr_class_name__) in Objs:
191
+ return f'{self.__repr_class_name__}(id={self.id}, ...)'
192
+ Objs.add((self.id, self.__repr_class_name__))
193
+ return f'{self.__repr_class_name__}(id={self.id}, {", ".join([f"{i}={obj.__get_repr__(Objs, now+1) if hasattr(obj:=getattr(self, i), '__get_repr__') else f'{getattr(self, i)}'}" for i in self.__db_fields__])})'
194
+
195
+ def _db_attribute_container_update(self, key, data=None):
196
+ """
197
+ call this functions, when any container attribute is updated, to update this attribute in db
198
+ :param key: name attribute container which update
199
+ :type key: str
200
+ :param data: the attribute (DbDict, DbSet and others containers)
201
+ """
202
+ self_dict = object.__getattribute__(self, '__dict__')
203
+ cls = object.__getattribute__(self, '__class__')
204
+ if ('_'+key in self_dict) or (key not in cls.__dict__['__db_fields__']):
205
+ return
206
+ cls.__dict__[key].container_update(self, data)
207
+
208
+ @classmethod
209
+ def _db_attribute_found_ids_by_attribute(cls, attribute_name:str, attribute_value):
210
+ tempdata = cls.__dbworkobj__.found_ids_by_value(class_name=cls.__name__, attribute_name=attribute_name, data=attribute_value, _cls_dbattribute=cls)
211
+ if tempdata['status_code'] != 200:
212
+ return set()
213
+ return tempdata['data']
214
+
215
+ @classmethod
216
+ def get(cls, id):
217
+ """
218
+ return the one object with this id or None, if it's obj is not found
219
+ if the 'get' method finds multiple search results, it selects the smallest id.
220
+ if the 'get' method does not find any search results, it returns None.
221
+ :param id:
222
+ :type id: int | db_types.Id | discriptor.Condition
223
+ :return: obj | None
224
+ """
225
+ if isinstance(id, discriptor.Condition):
226
+ Ids = id.found()
227
+ if not Ids:
228
+ return None
229
+ id = Ids.list_ids[0]
230
+ if isinstance(id, db_types.Id):
231
+ id = id.Id
232
+ if isinstance(id, int) or isinstance(id, db_types.Id):
233
+ obj = cls.__new__(cls)
234
+ obj.id = id
235
+ return obj
236
+ return None
237
+
238
+ @classmethod
239
+ def get_all_ids(cls):
240
+ temp = cls.__dbworkobj__.get_all_ids(cls.__name__)
241
+ if temp['status_code'] != 200:
242
+ return db_types.Ids()
243
+ return db_types.Ids(temp['data'])
244
+
245
+ def dump(self, attributes:set[str]=None):
246
+ """
247
+ Use it func, if you need dump the data to db, with manual_dump_mode
248
+ """
249
+ self_dict = object.__getattribute__(self, '__dict__')
250
+ cls = object.__getattribute__(self, '__class__')
251
+ all_attributes = object.__getattribute__(self, '__db_fields__')
252
+ for db_attr in all_attributes if attributes is None else all_attributes & attributes:
253
+ if '_'+db_attr in self_dict:
254
+ cls.__dict__[db_attr].dump_attr(self)
255
+
256
+ def set_manual_dump_mode(self, attributes:set[str]=None):
257
+ """
258
+ auto_dump_mode: attributes don't save in self.__dict__, all changes automatic dump in db.
259
+ manual_dump_mode: all attributes save in self.__dict__, and won't dump in db until self.db_attribute_set_auto_dump_mode is called.
260
+ function set undump_mode (dump_mode = False)
261
+ :param attributes: for which attributes will set the mode, ex: atributes={'name', 'age'}
262
+ """
263
+ all_attributes = object.__getattribute__(self, '__db_fields__')
264
+ self_dict = object.__getattribute__(self, '__dict__')
265
+ for db_attr in (all_attributes if attributes is None else all_attributes.keys() & attributes):
266
+ self_dict['_'+db_attr] = getattr(self, db_attr)
267
+
268
+ def set_auto_dump_mode(self, attributes:set[str]=None):
269
+ """
270
+ auto_dump_mode: attributes don't save in self.__dict__, all changes automatic dump in db.
271
+ manual_dump_mode: all attributes save in self.__dict__, and won't dump in db until self.db_attribute_set_auto_dump_mode is called.
272
+ function set auto_dump_mode and call self.db_attribute_dump() for dump attributes
273
+ :param attributes: for which attributes will set the mode, ex: atributes={'name', 'age'}
274
+ """
275
+ self.dump(attributes=attributes)
276
+ self_dict = object.__getattribute__(self, '__dict__')
277
+ all_attributes = object.__getattribute__(self, '__db_fields__')
278
+ for db_attr in all_attributes if attributes is None else all_attributes & attributes:
279
+ if '_'+db_attr in self_dict:
280
+ del self_dict['_'+db_attr]
281
+
282
+ def delete(self, attributes:set[str]=None):
283
+ """
284
+ Delete this id from db
285
+ :param attributes: attributes to be deleted, ex: obj.delete({'name', 'age'})
286
+ :return:
287
+ """
288
+ all_attributes = object.__getattribute__(self, '__db_fields__')
289
+ for db_attr in all_attributes if attributes is None else all_attributes & attributes:
290
+ delattr(self, db_attr)
291
+
292
+ @classmethod
293
+ def delete_objs(cls, IDs: set[int] | int, attributes:set[str]=None):
294
+ all_attributes = object.__getattribute__(cls, '__db_fields__')
295
+ attributes = all_attributes if attributes is None else attributes & all_attributes
296
+ IDs = {IDs} if isinstance(IDs, int) else IDs
297
+ dbworkobj = cls.__dbworkobj__
298
+ clsname = cls.__name__
299
+ for ID in IDs:
300
+ for db_attr in attributes:
301
+ dbworkobj.del_attribute_value(class_name=clsname, attribute_name=db_attr, ID=ID)
302
+
303
+ @classmethod
304
+ def found(cls, **kwargs):
305
+ """
306
+ WARNING: This function has lost its relevance and is not supported. Use '(cls.attr == value).found()' or 'cls.get(cls.attr == value)'
307
+
308
+ found ids objs with this values of attributes, ex:
309
+ objs: User(id=1, name='Bob', age=3), User(id=2, name='Bob', age=2), User(id=3, name='Anna', age=2)
310
+ User.found(name='Bob') -> {1, 2}
311
+ User.found(age=2) -> {2, 3}
312
+ User.found(name='Bob', age=2) -> {2}
313
+ :param kwargs: names and values of attributes (see doc.)
314
+ :return: set of ids
315
+ """
316
+ if not kwargs: return set()
317
+ res = cls._db_attribute_found_ids_by_attribute(attribute_name=(temp:=next(iter(kwargs))), attribute_value=kwargs[temp])
318
+ for key in kwargs:
319
+ res &= cls._db_attribute_found_ids_by_attribute(attribute_name=key, attribute_value=kwargs[key])
320
+ return res
@@ -0,0 +1,31 @@
1
+ from mysql.connector import connect
2
+
3
+ """Used mysql"""
4
+
5
+ """1** - connect errors, 2** - ok work functions, 3** and 4** - errors in project"""
6
+
7
+ status_cod = {100: {"eng": "There is no connection to the database", "ru": "Нет соединения с базой данных"},
8
+ 200: {"eng": "it's ok, no error", "ru": "Функция работает корректно, нет ошибок"},
9
+ 300: {"eng": "This attribute / object type is not supported", "ru": "Данный тип атрибута/объекта не поддерживается"},
10
+ 301: {"eng": "The table already exists", "ru": "Таблица уже существует"},
11
+ 302: {"eng": "The table doesn't exist", "ru": "Таблицы не существует"},
12
+ 303: {"eng": "The object is already in the table", "ru": "Объект уже находится в таблице"},
13
+ 304: {"eng": "Object not found", "ru": "Объект не найден"},
14
+ 305: {"eng": "The object argument has neither a default value nor a value in the database.", "ru": "Аргумент объекта не имеет ни значения по умолчанию, ни значения в database."},
15
+ 400: {"eng": "Erroneous use of the function", "ru": "Ошибка использования функции"},
16
+ 402: {"eng": "Unexpected function error", "ru": "Непредвиденная ошибка функции"},
17
+ 403: {"eng": "Not supported by this version of the program", "ru": "Не поддерживается данной версией программы"}}
18
+
19
+ class Connection:
20
+ """Used mysql db"""
21
+ def __init__(self, /, *, host='127.0.0.1', port=3306, user, password, database, **kwargs):
22
+ """*for params see 'https://dev.mysql.com/doc/connector-python/en/connector-python-connectargs.html'*"""
23
+ self.sql_name = 'MySQL'
24
+ try:
25
+ self.conn = connect(host=host, port=port, user=user, password=password, database=database, **kwargs)
26
+ self.cur = self.conn.cursor()
27
+ self.notconn = False
28
+ except:
29
+ self.conn = None
30
+ self.cur = None
31
+ self.notconn = True