DataBase attribute package
Project description
DbAttribute - Database Attribute
This module allows you to save attributes of objects not in RAM, but in a database. the closest analogue is SQLAlchemy. Unlike SQLAlchemy, this module maximizes automatism, allowing the developer to focus on other details without worrying about working with the database.
Supported types
This module supported standart types: int, float, str, bool, None, tuple, list, set, dict, datetime.
If developer needs other data types, he will need to write an adapter class.
Install
Installation from source (requires git):
$ pip install git+https://github.com/shutkanos/Db-Attribute.git
How it used
Create class
For create any classes (Tables):
- Set metaclass
DbAttributeMetaclass - Inheritance the
DbAttribute(optional, since it inherits automatically when using a metaclass) - Set dbworkobj for connect to database
- Create any fields / Annotations / DbFields for database
from db_attribute import DbAttribute, DbAttributeMetaclass, db_work, connector
from db_attribute.db_types import DbField
connect_obj = connector.Connection(host=*mysqlhost*, user=*user*, password=*password*, database=*databasename*)
db_work_obj = db_work.Db_work(connect_obj)
class User(DbAttribute, metaclass=DbAttributeMetaclass, __dbworkobj__=db_work_obj):
name: str = DbField(default='NotSet') # Ok
age: int = -1 # Ok
ban = DbField(default=False) # Ok
other_int_information = 100 # Need annotation or DbField - not error, but not saved
list_of_books = DbField(default_factory=lambda: ['name of first book']) # Ok
sittings: dict = DbField(default_factory=lambda: {}) # Ok
Each class object has its own id. It is inherited from DbAttribute and stored in dict
Options
Options can be set in different ways:
class User(DbAttribute, metaclass=DbAttributeMetaclass, __dbworkobj__ = db_work_obj):
pass
class User(DbAttribute, metaclass=DbAttributeMetaclass):
__dbworkobj__ = db_work_obj
class User(DbAttribute, metaclass=DbAttributeMetaclass):
class Meta:
__dbworkobj__ = db_work_obj
class BaseMeta:
__dbworkobj__ = dbworkobj
class User(DbAttribute, metaclass=DbAttributeMetaclass):
Meta = BaseMeta
All options:
__dbworkobj__- database work object (required parameter),__max_repr_recursion_limit__- maximum recursion limit for__repr__of DbAttribute__repr_class_name__- sets the name of this class when using the method__repr__of DbAttribute
Work with obj
Create new obj / add obj do db
For create obj use id (optional) and other fields (optional),
obj = User(id=3) # other field set to defaults value
print(obj) # User(id=3, name=*default value*)
obj = User(name='Ben', id=3)
print(obj) # User(id=3, name='Ben')
obj = User(name='Alica')
print(obj) # User(id=4, name='Alica')
obj = User(name='Alica')
print(obj) # User(id=5, name='Alica')
If Developer need recreated obj, he can call DbAttribute cls with id.
obj = User(name='Ben', age=10, id=3) #insert obj to db
print(obj) #User(id=3, name='Ben', age=10)
obj = User(id=3)
print(obj) #User(id=3, name='Ben', age=10)
obj = User('Anna', id=3)
print(obj) #User(id=3, name='Anna', age=10)
obj = User(age=15, id=3)
print(obj) #User(id=3, name='Anna', age=15)
obj = User(id=3)
print(obj) #User(id=3, name='Anna', age=15)
Found / get obj
if the developer needs to find an object, he can use the 'get' method.
if the 'get' method finds multiple search results, it selects the smallest id. if the 'get' method does not find any search results, it returns None.
#create objs
obj = User(name='Bob', age=3, id=1)
obj = User(name='Bob', age=2, id=2)
obj = User(name='Anna', age=2, id=3)
#finds objs
print(User.get((User.age == 3) & (User.name == 'Bob'))) #User(id=1, name=Bob, age=3)
print(User.get(User.name == 'Anna')) #User(id=3, name=Anna, age=2)
print(User.get(User.name == 'Bob')) #User(id=1, name=Bob, age=3)
print(User.get(User.name == 'Other name')) #None
To check the correctness of writing a logical expression, you can:
print(User.name == 'Anna') #(User.name = Anna)
print((User.age == 3) & (User.name == 'Bob')) #((User.age = 3) and (User.name = Bob))
Use '&', '|' instead of the 'and', 'or' operators. The 'and' and 'or' operators are not supported
Iterations
If a developer needs to iterate through all the elements of a class, they can use standard Python tools.
print(list(User))
#[User(id=1, name=Bob, age=3), User(id=2, name=Bob, age=2), User(id=3, name=Anna, age=2)]
print([i.name for i in User])
#['Bob', 'Bob', 'Anna']
for i in User:
print(i)
#User(id=1, name=Bob, age=3)
#User(id=2, name=Bob, age=2)
#User(id=3, name=Anna, age=2)
Change attribute of obj
obj = User(name='Bob', list_of_books=[], id=1)
print(obj) #User(id=1, name='Bob', list_of_books=[])
obj.name = 'Anna'
obj.list_of_books.append('Any name of book')
print(obj) #User(id=1, name='Anna', list_of_books=['Any name of book'])
Dump mode
If in any function you will work with obj, you can activate manual_dump_mode (auto_dump_mode is default),
auto_dump_mode: attributes don't save in self.dict, all changes automatic dump in db.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
DbAttribute.set_auto_dump_mode set auto_dump_mode and call dump
DbAttribute.set_manual_dump_mode set manual_dump_mode
user = User(id=1, any_db_data1=531, any_db_data2='string')
print(user.__dict__)
# {'id': 1}
user.set_manual_dump_mode()
print(user.__dict__)
# {'id': 1, '_any_db_data1': 531, '_any_db_data2': 'string'}
Or set dump mod for individual attributes
user = User(id=1, any_db_data1=531, any_db_data2='string')
print(user.__dict__)
# {'id': 1}
user.set_manual_dump_mode({'any_db_data1'})
print(user.__dict__)
# {'id': 1, '_any_db_data1': 531}
user = User(id=1, list_of_books=[])
user.set_manual_dump_mode()
for i in range(10 ** 5):
user.list_of_books.append(i)
user.set_auto_dump_mode()
If Developer need dump attributes to db with manual_dump_mode, you can use DbAttribute.db_attribute_dump
user = User(id=1, list_of_books=[])
user.set_manual_dump_mode()
for i in range(10 ** 4):
user.list_of_books.append(i)
user.dump() # dump the list_of_books to db
for i in range(10 ** 4):
user.list_of_books.append(i)
user.set_auto_dump_mode()
Types
Db attribute
Developer can set the Db attribute class as data type for another Db attribute class
from db_attribute.db_types import TableType
class Class_A(DbAttribute, metaclass=DbAttributeMetaclass):
Meta = BaseMeta
obj_b: TableType('Class_B')
class Class_B(DbAttribute, metaclass=DbAttributeMetaclass):
Meta = BaseMeta
obj_a: Class_A
For create obj:
obj_a = Class_A(id=15, name='Anna', obj_b=1)
obj_b = Class_B(id=1, name='Bob', obj_a=15)
print(obj_b) #Class_B(id=1, name=Bob, obj_a=Class_A(id=15, name=Anna, obj_b=Class_B(id=1, ...)))
#or
obj_a = Class_A(id=15, name='Anna', obj_b=obj_b)
print(obj_a) #Class_A(id=15, name=Anna, obj_b=Class_B(id=1, name=Bob, obj_a=Class_A(id=15, ...)))
For found obj:
Class_A(id=15, name='Anna', obj_b=1)
obj = Class_B(id=1, name='Bob', obj_a=15)
obj = Class_A.get(Class_A.obj_b == obj)
print(obj) #Class_A(id=15, name=Anna, obj_b=Class_B(id=1, name=Bob, obj_a=Class_A(id=15, ...)))
#And Found with use id of obj:
obj = Class_A.get(Class_A.obj_b == 1)
print(obj) #Class_A(id=15, name=Anna, obj_b=Class_B(id=1, name=Bob, obj_a=Class_A(id=15, ...)))
Db classes
When collections are stored in memory, they converted to Db classes
obj = User(1, list_of_books=[1, 2, 3])
print(type(obj.list_of_books)) #DbList
obj = User(1, times=[datetime(2024, 1, 1)])
print(type(obj.times[0])) #DbDatetime
And when collections dumped to db, they converted to json
obj = User(1, list_of_books=[1, 2, 3])
print(obj.list_of_books.dumps()) #{"t": "DbList", "d": [1, 2, 3]}
obj = User(1, times=[datetime(2024, 1, 1), datetime(2027, 7, 7)])
print(obj.list_of_books.dumps())
#{"t": "DbList", "d": [{"t": "DbDatetime", "d": "2024-01-01T00:00:00"}, {"t": "DbDatetime", "d": "2027-07-07T00:00:00"}]}
Json type
Db attribute support tuple, list, dict, other collections, but this types slow, because uses Db classes (see speed test).
To solve this problem, use a Json convertation
from db_attribute.db_types import JsonType, DbField
class User(DbAttribute, metaclass=DbAttributeMetaclass):
Meta = BaseMeta
sittings: JsonType = DbField(default_factory=lambda: {})
obj = User(1, sittings={1: 2, 3: [4, 5]})
print(obj.sittings) # {'1': 2, '3': [4, 5]}
print(type(obj.sittings)) # dict
- If Developer change obj with JsonType, this obj don't dump to db, you need set the new obj
- The json support only
dict,list,str,int,float,True,False,None
obj = User(1, sittings={1: 2, 3: [4, 5]})
del obj.sittings['3'] # not changed
obj.sittings['1'] = 3 # not changed
obj.sittings |= {4: 5} # not changed
print(obj.sittings) #{'1': 2, '3': [4, 5]}
obj.sittings = {1: 3} # changed
print(obj.sittings) #{'1': 3}
Speed Test
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
- mysql
select- 12500 op/sec - mysql
insert- 8500 op/sec
Get attr
Mysql select - 12500 op/sec
| Type | Operation/seconds | How much slower is it |
|---|---|---|
| int | 11658 op/sec | -6% |
| str | 11971 op/sec | -4% |
| tuple | 9685 op/sec | -22% |
| list | 9630 op/sec | -23% |
| dict | 9545 op/sec | -23% |
| JsonType | 11937 op/sec | -4% |
Set attr
Mysql insert - 8500 op/sec
| Type | Operation/seconds | How much slower is it |
|---|---|---|
| int | 8056 op/sec | -5% |
| str | 8173 op/sec | -3% |
| tuple | 6284 op/sec | -26% |
| list | 6043 op/sec | -28% |
| dict | 6354 op/sec | -25% |
| JsonType | 7297 op/sec | -14% |
Data base
this module used MySQL db (Licanse), and for use it, you need install mysql
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file db_attribute-2.1.tar.gz.
File metadata
- Download URL: db_attribute-2.1.tar.gz
- Upload date:
- Size: 28.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25c948ed36bfda45bc77b15578672cbf5b14641d6ff1c27d9a4c98fe9d93adf4
|
|
| MD5 |
2dca28183cdfbb46973c63c53b8b60ad
|
|
| BLAKE2b-256 |
0b708d07d008bd8b4c6ed903ca8c01924cf36e818e6aac158d2bdfee8d4bf321
|
File details
Details for the file db_attribute-2.1-py3-none-any.whl.
File metadata
- Download URL: db_attribute-2.1-py3-none-any.whl
- Upload date:
- Size: 26.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
14a06a6ca9f63abff3cba64d2f7c8b5c6990be5a0b0e64e6e5b85495ff0388e2
|
|
| MD5 |
300927732eb8d6dd546e78a20a1653d4
|
|
| BLAKE2b-256 |
f3a19f3f4ff01e10f981b351e0daddfdc6161d6e80daa12f48f7ce1e9c3d1a57
|