Skip to main content

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

db_attribute-2.1.tar.gz (28.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

db_attribute-2.1-py3-none-any.whl (26.0 kB view details)

Uploaded Python 3

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

Hashes for db_attribute-2.1.tar.gz
Algorithm Hash digest
SHA256 25c948ed36bfda45bc77b15578672cbf5b14641d6ff1c27d9a4c98fe9d93adf4
MD5 2dca28183cdfbb46973c63c53b8b60ad
BLAKE2b-256 0b708d07d008bd8b4c6ed903ca8c01924cf36e818e6aac158d2bdfee8d4bf321

See more details on using hashes here.

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

Hashes for db_attribute-2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 14a06a6ca9f63abff3cba64d2f7c8b5c6990be5a0b0e64e6e5b85495ff0388e2
MD5 300927732eb8d6dd546e78a20a1653d4
BLAKE2b-256 f3a19f3f4ff01e10f981b351e0daddfdc6161d6e80daa12f48f7ce1e9c3d1a57

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page