db-attribute


Namedb-attribute JSON
Version 2.1.1.1 PyPI version JSON
download
home_pagehttps://github.com/shutkanos/Db-Attribute
SummaryDataBase attribute package
upload_time2025-08-28 07:45:18
maintainerNone
docs_urlNone
authorShutkanos
requires_python>=3.9
licenseMIT
keywords db database attribute db_attribute db attribute dbattribute database attribute orm orm attribute orm attribute orm attr attr orm attr orm db attr dbattr
VCS
bugtrack_url
requirements mysql-connector-python orjson
Travis-CI No Travis.
coveralls test coverage No coveralls.
            DbAttribute - Database Attribute
=========================

DbAttribute is an ORM library designed to simplify database interactions. Core capabilities:

* Automatic state synchronization
Object attribute changes are automatically tracked and persisted to the database without requiring explicit commit calls.
<br><br>
* Direct object manipulation
Supports both value assignment (obj.attr = value) and in-place modification of container types:

```python
obj.books.append("New Book")
obj.settings["theme"] = "dark"
```

* Expressive query syntax
Filtering uses Python operators with natural syntax:

```python
# Find users older than 18 named John
User.get((User.age > 18) & (User.name == "John"))

# Get all users named Bob
[user for user in User if user.name == "Bob"]
```
The library provides tools for declarative model definition, relationship management, and database operation optimization through configurable synchronization modes.

# Table of contents

* [Table of contents](#table-of-contents)
* [Supported types](#supported-types)
* [Install](#install)
* [How to use it](#how-to-use-it)
    * [Create class](#create-class)
        * [Options](#options)
    * [Work with obj](#work-with-obj)
        * [Create new object](#create-new-object)
        * [Finding objects](#finding-objects)
        * [Iterations](#iterations)
        * [Change attribute of obj](#change-attribute-of-obj)
        * [Dump mode](#dump-mode)
    * [Types](#types)
        * [Db attribute](#db-attribute)
        * [Db classes](#db-classes)
        * [Custom Db Classes](#custom-db-classes) 
        * [Json type](#json-type)
* [Speed Test](#speed-test)
    * [Get attr](#get-attr)
    * [Set attr](#set-attr)
* [Data base](#data-base)

# Supported types

This module supports standard types: `int`, `float`, `str`, `bool`, `None`, `tuple`, `list`, `set`, `dict`, `datetime`.

If a developer needs other data types, they will need to write an adapter class.

# Install

The package can be obtained from PyPI and installed in a single step:

```
pip install db_attribute
```

It can also be obtained from source (requires git):

```
pip install git+https://github.com/shutkanos/Db-Attribute.git
```

# How to use it

## Create class

To create any class (Table):

* Set metaclass `DbAttributeMetaclass`
* Inheritance the `DbAttribute` (optional, since it inherits automatically when using a metaclass)
* Set dbworkobj for connect to database
* Define fields using annotations or DbField for database columns

```python
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
    settings: dict = DbField(default_factory=dict) # Ok
```

Each instance has a unique `id` identifier. It is inherited from DbAttribute and stored in `__dict__`

### Options

Options can be set in different ways:

```python
class User(DbAttribute, metaclass=DbAttributeMetaclass, __dbworkobj__ = db_work_obj):
    pass
```
```python
class User(DbAttribute, metaclass=DbAttributeMetaclass):
    __dbworkobj__ = db_work_obj
```
```python
class User(DbAttribute, metaclass=DbAttributeMetaclass):
    class Meta:
        __dbworkobj__ = db_work_obj
```
```python
class BaseMeta:
    __dbworkobj__ = dbworkobj
class Class_A(DbAttribute, metaclass=DbAttributeMetaclass):
    Meta = BaseMeta
class Class_B(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 object

To create an object, use an id (optional) and other fields (optional),

```python
obj = User(id=3) # other field set to defaults value
print(obj) # User(id=3, name=*default value*)
```
```python
obj = User(name='Ben', id=3)
print(obj) # User(id=3, name='Ben')
```
```python
obj = User(name='Alica')
print(obj) # User(id=4, name='Alica')
obj = User(name='Alica')
print(obj) # User(id=5, name='Alica')
```

If a developer needs to recreate an object, he can call DbAttribute cls with id.

```python
obj = User(name='Ben', age=20, id=3) #insert obj to db
print(obj) #User(id=3, name='Ben', age=20)

obj = User(id=3)
print(obj) #User(id=3, name='Ben', age=20)

obj = User('Anna', id=3)
print(obj) #User(id=3, name='Anna', age=20)

obj = User(age=25, id=3)
print(obj) #User(id=3, name='Anna', age=25)

obj = User(id=3)
print(obj) #User(id=3, name='Anna', age=25)
```

### Finding objects

If a developer needs to find an object, they can use the 'get' method.

The `get()` method returns:
- Single object if found
- Object with smallest ID if multiple matches exist
- `None` if no matches found

```python
#create objs
obj = User(name='Bob', age=20, id=1)
obj = User(name='Bob', age=30, id=2)
obj = User(name='Anna', age=20, id=3)
#finds objs
print(User.get(id=2))                                   #User(id=2, name='Bob', age=30)
print(User.get((User.age == 30) & (User.name == 'Bob')))#User(id=2, name='Bob', age=30)
print(User.get(User.name == 'Anna'))                    #User(id=3, name='Anna', age=20)
print(User.get(User.name == 'Bob'))                     #User(id=1, name='Bob', age=20)
print(User.get(User.name == 'Other name'))              #None
```

To check the correctness of writing a logical expression, you can:

```python
print(User.name == 'Anna')                     #(User.name = 'Anna')
print((User.age == 30) & (User.name == 'Bob')) #((User.age = 30) and (User.name = 'Bob'))
```

Use '&' and '|' instead of the 'and' 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.

```python
print(list(User))
#[User(id=1, name='Bob', age=30), User(id=2, name='Bob', age=20), User(id=3, name='Anna', age=20)]

print([i for i in User if i.age < 30])
#[User(id=2, name='Bob', age=20), User(id=3, name='Anna', age=20)]

for i in User:
    print(i)
#User(id=1, name='Bob', age=30)
#User(id=2, name='Bob', age=20)
#User(id=3, name='Anna', age=20)
```
⚠️ Iterations loads all objects - not recommended for large tables

### Change attribute of obj

```python
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 the 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

```python
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 mode for individual attributes

```python
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}
```

```python
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 a developer needs to dump attributes to db with manual_dump_mode, you can use DbAttribute.db_attribute_dump

```python
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

A developer can set the Db attribute class as data type for another Db attribute class

```python
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
```
To create an object:
```python
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:
```python
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, ...)))
```
One-to-Many relationship:
```python
from db_attribute.db_types import DbField

class Author(DbAttribute, metaclass=DbAttributeMetaclass):
    Meta = BaseMeta
    name: str = ""
    books: list = DbField(default_factory=list)

class Book(DbAttribute, metaclass=DbAttributeMetaclass):
    Meta = BaseMeta
    title: str = ""
    author: Author

author = Author(name="George Orwell")
book = Book(title="1984", author=author)
author.books.append(book)

print(author) #Author(id=1, name='George Orwell', books=[Book(id=1, title='1984', author=Author(id=1, ...))])
print(book) #Book(id=1, title='1984', author=Author(id=1, name='George Orwell', books=[Book(id=1, ...)]))
```

### Db classes
When collections are stored in memory, they converted to Db classes
```python
obj = User(1, list_of_books=[1, 2, 3])
print(type(obj.list_of_books)) #DbList
```
```python
obj = User(1, times=[datetime(2024, 1, 1)])
print(type(obj.times[0])) #DbDatetime
```
And when collections dumped to db, they converted to json
```python
obj = User(1, list_of_books=[1, 2, 3])
print(obj.list_of_books.dumps()) #{"t": "DbList", "d": [1, 2, 3]}
```
```python
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"}]}
```

### Custom Db Classes

And to create a custom 'Db class', you need to
* Create regular class
* Inherit from DbClass (DbClass - first. It is important) and your regular class for custom Db class
* Set a Decorator with or without the necessary parameters
* Set at least the `__convert_to_db__` module, according to the documentation
* add additional modules.

```python
from db_attribute import db_class

# for exemple you have your class:

class UserDataClass:
    def __init__(self, value = None):
        self.value = value
    def __repr__(self):
        return f'UserDataClass(value={self.value})'

@db_class.DbClassDecorator
class DbUserDataClass(db_class.DbClass, UserDataClass):
    def __init__(self, value=None, **kwargs):
        # This is not a mandatory method
        super().__init__(_call_init=False, **kwargs) # But this call is mandatory
        self.__dict__['value'] = value
        # Here we set the value of a variable using __dict__.
        # This is not necessary, but it speeds up the work with the class.

    @classmethod
    def __convert_to_db__(cls, obj: UserDataClass, **kwargs):
        """Methode for convert obj to dbclass - need @classmethod and kwargs"""
        # This is a mandatory method
        # Call with _user_db=True
        # Example:
        # print(type(DbUserDataClass(value=10)))                #UserDataClass
        # print(type(DbUserDataClass(value=10, _use_db=True)))  #DbUserDataClass
        return cls(_use_db=True, value=obj.value, **kwargs)

    def __convert_from_db__(self):
        """Reverse convert"""
        # This is not a mandatory method.
        return self._standart_class(value=self.value)
```

For example:

```python
class User(DbAttribute, metaclass=DbAttributeMetaclass):
    Meta = BaseMeta
    data: UserDataClass

user = User(id=1, data=UserDataClass(10))
print(user.data) # UserDataClass(value=10)
user.data.value = 5
print(user.data) # UserDataClass(value=5)
```

### Json type

DbAttribute supports `tuple`, `list`, `dict`, other collections, but these types are slow, because uses Db classes (see [speed test](#speed-test)).

To solve this problem, use a Json convertation

```python
from db_attribute.db_types import JsonType, DbField

class User(DbAttribute, metaclass=DbAttributeMetaclass):
    Meta = BaseMeta
    settings: JsonType = DbField(default_factory=lambda: {})

obj = User(1, settings={1: 2, 3: [4, 5]})
print(obj.settings)  # {'1': 2, '3': [4, 5]}
print(type(obj.settings))  # dict
```

* If Developer change obj with JsonType, this obj don't dump to db, you need set the new obj 
* JsonType only supports: `dict`, `list`, `str`, `int`, `float`, `bool`, `None`

```python
obj = User(1, settings={1: 2, 3: [4, 5]})
del obj.settings['3'] # not changed
obj.settings['1'] = 3 # not changed
obj.settings |= {4: 5} # not changed
print(obj.settings) #{'1': 2, '3': [4, 5]}
obj.settings = {1: 3} # changed
print(obj.settings) #{'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<br>

## Get attr

Mysql `select` - 12500 op/sec

Type      | Operation/seconds | Performance impact
----------|-------------------|---------------------------
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<br>

Type      | Operation/seconds | Performance impact
----------|-------------------|---------------------------
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 uses MySQL db (<a href="https://github.com/mysql/mysql-connector-python/blob/trunk/LICENSE.txt">License</a>), and for use it, you need install <a href='https://www.mysql.com'>mysql</a>


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/shutkanos/Db-Attribute",
    "name": "db-attribute",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "db, database, attribute, db_attribute, db attribute, DbAttribute, database attribute, orm, ORM, attribute orm, attribute ORM, attr, attr ORM, attr orm, db attr, DbAttr",
    "author": "Shutkanos",
    "author_email": "Shutkanos <Shutkanos836926@mail.ru>",
    "download_url": "https://files.pythonhosted.org/packages/b3/b1/d4e328c65cd509bb82770188ac7ce43a9143e3a2f687fab60ed707174083/db_attribute-2.1.1.1.tar.gz",
    "platform": null,
    "description": "DbAttribute - Database Attribute\r\n=========================\r\n\r\nDbAttribute is an ORM library designed to simplify database interactions. Core capabilities:\r\n\r\n* Automatic state synchronization\r\nObject attribute changes are automatically tracked and persisted to the database without requiring explicit commit calls.\r\n<br><br>\r\n* Direct object manipulation\r\nSupports both value assignment (obj.attr = value) and in-place modification of container types:\r\n\r\n```python\r\nobj.books.append(\"New Book\")\r\nobj.settings[\"theme\"] = \"dark\"\r\n```\r\n\r\n* Expressive query syntax\r\nFiltering uses Python operators with natural syntax:\r\n\r\n```python\r\n# Find users older than 18 named John\r\nUser.get((User.age > 18) & (User.name == \"John\"))\r\n\r\n# Get all users named Bob\r\n[user for user in User if user.name == \"Bob\"]\r\n```\r\nThe library provides tools for declarative model definition, relationship management, and database operation optimization through configurable synchronization modes.\r\n\r\n# Table of contents\r\n\r\n* [Table of contents](#table-of-contents)\r\n* [Supported types](#supported-types)\r\n* [Install](#install)\r\n* [How to use it](#how-to-use-it)\r\n    * [Create class](#create-class)\r\n        * [Options](#options)\r\n    * [Work with obj](#work-with-obj)\r\n        * [Create new object](#create-new-object)\r\n        * [Finding objects](#finding-objects)\r\n        * [Iterations](#iterations)\r\n        * [Change attribute of obj](#change-attribute-of-obj)\r\n        * [Dump mode](#dump-mode)\r\n    * [Types](#types)\r\n        * [Db attribute](#db-attribute)\r\n        * [Db classes](#db-classes)\r\n        * [Custom Db Classes](#custom-db-classes) \r\n        * [Json type](#json-type)\r\n* [Speed Test](#speed-test)\r\n    * [Get attr](#get-attr)\r\n    * [Set attr](#set-attr)\r\n* [Data base](#data-base)\r\n\r\n# Supported types\r\n\r\nThis module supports standard types: `int`, `float`, `str`, `bool`, `None`, `tuple`, `list`, `set`, `dict`, `datetime`.\r\n\r\nIf a developer needs other data types, they will need to write an adapter class.\r\n\r\n# Install\r\n\r\nThe package can be obtained from PyPI and installed in a single step:\r\n\r\n```\r\npip install db_attribute\r\n```\r\n\r\nIt can also be obtained from source (requires git):\r\n\r\n```\r\npip install git+https://github.com/shutkanos/Db-Attribute.git\r\n```\r\n\r\n# How to use it\r\n\r\n## Create class\r\n\r\nTo create any class (Table):\r\n\r\n* Set metaclass `DbAttributeMetaclass`\r\n* Inheritance the `DbAttribute` (optional, since it inherits automatically when using a metaclass)\r\n* Set dbworkobj for connect to database\r\n* Define fields using annotations or DbField for database columns\r\n\r\n```python\r\nfrom db_attribute import DbAttribute, DbAttributeMetaclass, db_work, connector\r\nfrom db_attribute.db_types import DbField\r\n\r\nconnect_obj = connector.Connection(host=*mysqlhost*, user=*user*, password=*password*, database=*databasename*)\r\ndb_work_obj = db_work.Db_work(connect_obj)\r\n\r\nclass User(DbAttribute, metaclass=DbAttributeMetaclass, __dbworkobj__=db_work_obj):\r\n    name: str = DbField(default='NotSet') # Ok\r\n    age: int = -1 # Ok\r\n    ban = DbField(default=False) # Ok\r\n    other_int_information = 100 # Need annotation or DbField - not error, but not saved\r\n    list_of_books = DbField(default_factory=lambda: ['name of first book']) # Ok\r\n    settings: dict = DbField(default_factory=dict) # Ok\r\n```\r\n\r\nEach instance has a unique `id` identifier. It is inherited from DbAttribute and stored in `__dict__`\r\n\r\n### Options\r\n\r\nOptions can be set in different ways:\r\n\r\n```python\r\nclass User(DbAttribute, metaclass=DbAttributeMetaclass, __dbworkobj__ = db_work_obj):\r\n    pass\r\n```\r\n```python\r\nclass User(DbAttribute, metaclass=DbAttributeMetaclass):\r\n    __dbworkobj__ = db_work_obj\r\n```\r\n```python\r\nclass User(DbAttribute, metaclass=DbAttributeMetaclass):\r\n    class Meta:\r\n        __dbworkobj__ = db_work_obj\r\n```\r\n```python\r\nclass BaseMeta:\r\n    __dbworkobj__ = dbworkobj\r\nclass Class_A(DbAttribute, metaclass=DbAttributeMetaclass):\r\n    Meta = BaseMeta\r\nclass Class_B(DbAttribute, metaclass=DbAttributeMetaclass):\r\n    Meta = BaseMeta\r\n```\r\n\r\nAll options:\r\n\r\n* `__dbworkobj__` - database work object (required parameter),\r\n* `__max_repr_recursion_limit__` - maximum recursion limit for `__repr__` of DbAttribute\r\n* `__repr_class_name__` - sets the name of this class when using the method `__repr__` of DbAttribute\r\n\r\n## Work with obj\r\n\r\n### Create new object\r\n\r\nTo create an object, use an id (optional) and other fields (optional),\r\n\r\n```python\r\nobj = User(id=3) # other field set to defaults value\r\nprint(obj) # User(id=3, name=*default value*)\r\n```\r\n```python\r\nobj = User(name='Ben', id=3)\r\nprint(obj) # User(id=3, name='Ben')\r\n```\r\n```python\r\nobj = User(name='Alica')\r\nprint(obj) # User(id=4, name='Alica')\r\nobj = User(name='Alica')\r\nprint(obj) # User(id=5, name='Alica')\r\n```\r\n\r\nIf a developer needs to recreate an object, he can call DbAttribute cls with id.\r\n\r\n```python\r\nobj = User(name='Ben', age=20, id=3) #insert obj to db\r\nprint(obj) #User(id=3, name='Ben', age=20)\r\n\r\nobj = User(id=3)\r\nprint(obj) #User(id=3, name='Ben', age=20)\r\n\r\nobj = User('Anna', id=3)\r\nprint(obj) #User(id=3, name='Anna', age=20)\r\n\r\nobj = User(age=25, id=3)\r\nprint(obj) #User(id=3, name='Anna', age=25)\r\n\r\nobj = User(id=3)\r\nprint(obj) #User(id=3, name='Anna', age=25)\r\n```\r\n\r\n### Finding objects\r\n\r\nIf a developer needs to find an object, they can use the 'get' method.\r\n\r\nThe `get()` method returns:\r\n- Single object if found\r\n- Object with smallest ID if multiple matches exist\r\n- `None` if no matches found\r\n\r\n```python\r\n#create objs\r\nobj = User(name='Bob', age=20, id=1)\r\nobj = User(name='Bob', age=30, id=2)\r\nobj = User(name='Anna', age=20, id=3)\r\n#finds objs\r\nprint(User.get(id=2))                                   #User(id=2, name='Bob', age=30)\r\nprint(User.get((User.age == 30) & (User.name == 'Bob')))#User(id=2, name='Bob', age=30)\r\nprint(User.get(User.name == 'Anna'))                    #User(id=3, name='Anna', age=20)\r\nprint(User.get(User.name == 'Bob'))                     #User(id=1, name='Bob', age=20)\r\nprint(User.get(User.name == 'Other name'))              #None\r\n```\r\n\r\nTo check the correctness of writing a logical expression, you can:\r\n\r\n```python\r\nprint(User.name == 'Anna')                     #(User.name = 'Anna')\r\nprint((User.age == 30) & (User.name == 'Bob')) #((User.age = 30) and (User.name = 'Bob'))\r\n```\r\n\r\nUse '&' and '|' instead of the 'and' and 'or' operators. The 'and' and 'or' operators are not supported\r\n\r\n### Iterations\r\n\r\nIf a developer needs to iterate through all the elements of a class, they can use standard Python tools.\r\n\r\n```python\r\nprint(list(User))\r\n#[User(id=1, name='Bob', age=30), User(id=2, name='Bob', age=20), User(id=3, name='Anna', age=20)]\r\n\r\nprint([i for i in User if i.age < 30])\r\n#[User(id=2, name='Bob', age=20), User(id=3, name='Anna', age=20)]\r\n\r\nfor i in User:\r\n    print(i)\r\n#User(id=1, name='Bob', age=30)\r\n#User(id=2, name='Bob', age=20)\r\n#User(id=3, name='Anna', age=20)\r\n```\r\n\u26a0\ufe0f Iterations loads all objects - not recommended for large tables\r\n\r\n### Change attribute of obj\r\n\r\n```python\r\nobj = User(name='Bob', list_of_books=[], id=1)\r\n\r\nprint(obj) #User(id=1, name='Bob', list_of_books=[])\r\n\r\nobj.name = 'Anna'\r\nobj.list_of_books.append('Any name of book')\r\n\r\nprint(obj) #User(id=1, name='Anna', list_of_books=['Any name of book'])\r\n```\r\n\r\n### Dump mode\r\n\r\nIf in any function you will work with obj, you can activate manual_dump_mode (auto_dump_mode is the default),\r\n\r\n* `auto_dump_mode`: attributes don't save in self.__dict__, all changes automatic dump in db.\r\n* `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\r\n\r\nDbAttribute.set_auto_dump_mode set auto_dump_mode and call dump\r\n\r\nDbAttribute.set_manual_dump_mode set manual_dump_mode\r\n\r\n```python\r\nuser = User(id=1, any_db_data1=531, any_db_data2='string')\r\nprint(user.__dict__)\r\n# {'id': 1}\r\nuser.set_manual_dump_mode()\r\nprint(user.__dict__)\r\n# {'id': 1, '_any_db_data1': 531, '_any_db_data2': 'string'}\r\n```\r\nOr set dump mode for individual attributes\r\n\r\n```python\r\nuser = User(id=1, any_db_data1=531, any_db_data2='string')\r\nprint(user.__dict__)\r\n# {'id': 1}\r\nuser.set_manual_dump_mode({'any_db_data1'})\r\nprint(user.__dict__)\r\n# {'id': 1, '_any_db_data1': 531}\r\n```\r\n\r\n```python\r\nuser = User(id=1, list_of_books=[])\r\nuser.set_manual_dump_mode()\r\nfor i in range(10 ** 5):\r\n    user.list_of_books.append(i)\r\nuser.set_auto_dump_mode()\r\n```\r\nIf a developer needs to dump attributes to db with manual_dump_mode, you can use DbAttribute.db_attribute_dump\r\n\r\n```python\r\nuser = User(id=1, list_of_books=[])\r\nuser.set_manual_dump_mode()\r\nfor i in range(10 ** 4):\r\n    user.list_of_books.append(i)\r\nuser.dump()  # dump the list_of_books to db\r\nfor i in range(10 ** 4):\r\n    user.list_of_books.append(i)\r\nuser.set_auto_dump_mode()\r\n```\r\n\r\n## Types\r\n\r\n### Db attribute\r\n\r\nA developer can set the Db attribute class as data type for another Db attribute class\r\n\r\n```python\r\nfrom db_attribute.db_types import TableType\r\n\r\nclass Class_A(DbAttribute, metaclass=DbAttributeMetaclass):\r\n    Meta = BaseMeta\r\n    obj_b: TableType('Class_B')\r\n\r\nclass Class_B(DbAttribute, metaclass=DbAttributeMetaclass):\r\n    Meta = BaseMeta\r\n    obj_a: Class_A\r\n```\r\nTo create an object:\r\n```python\r\nobj_a = Class_A(id=15, name='Anna', obj_b=1)\r\nobj_b = Class_B(id=1, name='Bob', obj_a=15)\r\nprint(obj_b) #Class_B(id=1, name='Bob', obj_a=Class_A(id=15, name='Anna', obj_b=Class_B(id=1, ...)))\r\n#or\r\nobj_a = Class_A(id=15, name='Anna', obj_b=obj_b)\r\nprint(obj_a) #Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))\r\n```\r\nFor found obj:\r\n```python\r\nClass_A(id=15, name='Anna', obj_b=1)\r\nobj = Class_B(id=1, name='Bob', obj_a=15)\r\nobj = Class_A.get(Class_A.obj_b == obj)\r\nprint(obj) #Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))\r\n#And Found with use id of obj:\r\nobj = Class_A.get(Class_A.obj_b == 1)\r\nprint(obj) #Class_A(id=15, name='Anna', obj_b=Class_B(id=1, name='Bob', obj_a=Class_A(id=15, ...)))\r\n```\r\nOne-to-Many relationship:\r\n```python\r\nfrom db_attribute.db_types import DbField\r\n\r\nclass Author(DbAttribute, metaclass=DbAttributeMetaclass):\r\n    Meta = BaseMeta\r\n    name: str = \"\"\r\n    books: list = DbField(default_factory=list)\r\n\r\nclass Book(DbAttribute, metaclass=DbAttributeMetaclass):\r\n    Meta = BaseMeta\r\n    title: str = \"\"\r\n    author: Author\r\n\r\nauthor = Author(name=\"George Orwell\")\r\nbook = Book(title=\"1984\", author=author)\r\nauthor.books.append(book)\r\n\r\nprint(author) #Author(id=1, name='George Orwell', books=[Book(id=1, title='1984', author=Author(id=1, ...))])\r\nprint(book) #Book(id=1, title='1984', author=Author(id=1, name='George Orwell', books=[Book(id=1, ...)]))\r\n```\r\n\r\n### Db classes\r\nWhen collections are stored in memory, they converted to Db classes\r\n```python\r\nobj = User(1, list_of_books=[1, 2, 3])\r\nprint(type(obj.list_of_books)) #DbList\r\n```\r\n```python\r\nobj = User(1, times=[datetime(2024, 1, 1)])\r\nprint(type(obj.times[0])) #DbDatetime\r\n```\r\nAnd when collections dumped to db, they converted to json\r\n```python\r\nobj = User(1, list_of_books=[1, 2, 3])\r\nprint(obj.list_of_books.dumps()) #{\"t\": \"DbList\", \"d\": [1, 2, 3]}\r\n```\r\n```python\r\nobj = User(1, times=[datetime(2024, 1, 1), datetime(2027, 7, 7)])\r\nprint(obj.list_of_books.dumps())\r\n#{\"t\": \"DbList\", \"d\": [{\"t\": \"DbDatetime\", \"d\": \"2024-01-01T00:00:00\"}, {\"t\": \"DbDatetime\", \"d\": \"2027-07-07T00:00:00\"}]}\r\n```\r\n\r\n### Custom Db Classes\r\n\r\nAnd to create a custom 'Db class', you need to\r\n* Create regular class\r\n* Inherit from DbClass (DbClass - first. It is important) and your regular class for custom Db class\r\n* Set a Decorator with or without the necessary parameters\r\n* Set at least the `__convert_to_db__` module, according to the documentation\r\n* add additional modules.\r\n\r\n```python\r\nfrom db_attribute import db_class\r\n\r\n# for exemple you have your class:\r\n\r\nclass UserDataClass:\r\n    def __init__(self, value = None):\r\n        self.value = value\r\n    def __repr__(self):\r\n        return f'UserDataClass(value={self.value})'\r\n\r\n@db_class.DbClassDecorator\r\nclass DbUserDataClass(db_class.DbClass, UserDataClass):\r\n    def __init__(self, value=None, **kwargs):\r\n        # This is not a mandatory method\r\n        super().__init__(_call_init=False, **kwargs) # But this call is mandatory\r\n        self.__dict__['value'] = value\r\n        # Here we set the value of a variable using __dict__.\r\n        # This is not necessary, but it speeds up the work with the class.\r\n\r\n    @classmethod\r\n    def __convert_to_db__(cls, obj: UserDataClass, **kwargs):\r\n        \"\"\"Methode for convert obj to dbclass - need @classmethod and kwargs\"\"\"\r\n        # This is a mandatory method\r\n        # Call with _user_db=True\r\n        # Example:\r\n        # print(type(DbUserDataClass(value=10)))                #UserDataClass\r\n        # print(type(DbUserDataClass(value=10, _use_db=True)))  #DbUserDataClass\r\n        return cls(_use_db=True, value=obj.value, **kwargs)\r\n\r\n    def __convert_from_db__(self):\r\n        \"\"\"Reverse convert\"\"\"\r\n        # This is not a mandatory method.\r\n        return self._standart_class(value=self.value)\r\n```\r\n\r\nFor example:\r\n\r\n```python\r\nclass User(DbAttribute, metaclass=DbAttributeMetaclass):\r\n    Meta = BaseMeta\r\n    data: UserDataClass\r\n\r\nuser = User(id=1, data=UserDataClass(10))\r\nprint(user.data) # UserDataClass(value=10)\r\nuser.data.value = 5\r\nprint(user.data) # UserDataClass(value=5)\r\n```\r\n\r\n### Json type\r\n\r\nDbAttribute supports `tuple`, `list`, `dict`, other collections, but these types are slow, because uses Db classes (see [speed test](#speed-test)).\r\n\r\nTo solve this problem, use a Json convertation\r\n\r\n```python\r\nfrom db_attribute.db_types import JsonType, DbField\r\n\r\nclass User(DbAttribute, metaclass=DbAttributeMetaclass):\r\n    Meta = BaseMeta\r\n    settings: JsonType = DbField(default_factory=lambda: {})\r\n\r\nobj = User(1, settings={1: 2, 3: [4, 5]})\r\nprint(obj.settings)  # {'1': 2, '3': [4, 5]}\r\nprint(type(obj.settings))  # dict\r\n```\r\n\r\n* If Developer change obj with JsonType, this obj don't dump to db, you need set the new obj \r\n* JsonType only supports: `dict`, `list`, `str`, `int`, `float`, `bool`, `None`\r\n\r\n```python\r\nobj = User(1, settings={1: 2, 3: [4, 5]})\r\ndel obj.settings['3'] # not changed\r\nobj.settings['1'] = 3 # not changed\r\nobj.settings |= {4: 5} # not changed\r\nprint(obj.settings) #{'1': 2, '3': [4, 5]}\r\nobj.settings = {1: 3} # changed\r\nprint(obj.settings) #{'1': 3}\r\n```\r\n\r\n# Speed Test\r\n\r\nThe 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\r\n\r\n* mysql `select` - 12500 op/sec\r\n* mysql `insert` - 8500 op/sec<br>\r\n\r\n## Get attr\r\n\r\nMysql `select` - 12500 op/sec\r\n\r\nType      | Operation/seconds | Performance impact\r\n----------|-------------------|---------------------------\r\nint       | 11658 op/sec      | -6%\r\nstr       | 11971 op/sec      | -4%\r\ntuple     | 9685 op/sec       | -22%\r\nlist      | 9630 op/sec       | -23%\r\ndict      | 9545 op/sec       | -23%\r\nJsonType  | 11937 op/sec      | -4%\r\n\r\n## Set attr\r\n\r\nMysql `insert` - 8500 op/sec<br>\r\n\r\nType      | Operation/seconds | Performance impact\r\n----------|-------------------|---------------------------\r\nint       | 8056 op/sec       | -5%\r\nstr       | 8173 op/sec       | -3%\r\ntuple     | 6284 op/sec       | -26%\r\nlist      | 6043 op/sec       | -28%\r\ndict      | 6354 op/sec       | -25%\r\nJsonType  | 7297 op/sec       | -14%\r\n\r\n# Data base\r\n\r\nThis module uses MySQL db (<a href=\"https://github.com/mysql/mysql-connector-python/blob/trunk/LICENSE.txt\">License</a>), and for use it, you need install <a href='https://www.mysql.com'>mysql</a>\r\n\r\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "DataBase attribute package",
    "version": "2.1.1.1",
    "project_urls": {
        "Documentation": "https://github.com/shutkanos/Db-Attribute#readme.md",
        "Homepage": "https://github.com/shutkanos/Db-Attribute",
        "Repository": "https://github.com/shutkanos/Db-Attribute"
    },
    "split_keywords": [
        "db",
        " database",
        " attribute",
        " db_attribute",
        " db attribute",
        " dbattribute",
        " database attribute",
        " orm",
        " orm",
        " attribute orm",
        " attribute orm",
        " attr",
        " attr orm",
        " attr orm",
        " db attr",
        " dbattr"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "df05324a2c069c30676b145eff596ce03c5a7a9a05ef1cd3f4cc4e82b94abe48",
                "md5": "f22ba5378eb7d8897e426d997862d6cf",
                "sha256": "0a7ebe7245c02d8f6dd8007268389366e8c36c104dab44337e4746b4d213918e"
            },
            "downloads": -1,
            "filename": "db_attribute-2.1.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f22ba5378eb7d8897e426d997862d6cf",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 28297,
            "upload_time": "2025-08-28T07:45:16",
            "upload_time_iso_8601": "2025-08-28T07:45:16.814410Z",
            "url": "https://files.pythonhosted.org/packages/df/05/324a2c069c30676b145eff596ce03c5a7a9a05ef1cd3f4cc4e82b94abe48/db_attribute-2.1.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "b3b1d4e328c65cd509bb82770188ac7ce43a9143e3a2f687fab60ed707174083",
                "md5": "0f212e82e07228b5eb229b80edca2981",
                "sha256": "c8084e5e44883ca2084039400033f45ead00e16547f675e036a49becf4b39c13"
            },
            "downloads": -1,
            "filename": "db_attribute-2.1.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "0f212e82e07228b5eb229b80edca2981",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 31321,
            "upload_time": "2025-08-28T07:45:18",
            "upload_time_iso_8601": "2025-08-28T07:45:18.132499Z",
            "url": "https://files.pythonhosted.org/packages/b3/b1/d4e328c65cd509bb82770188ac7ce43a9143e3a2f687fab60ed707174083/db_attribute-2.1.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-28 07:45:18",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "shutkanos",
    "github_project": "Db-Attribute",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [
        {
            "name": "mysql-connector-python",
            "specs": [
                [
                    ">=",
                    "8.4.0"
                ]
            ]
        },
        {
            "name": "orjson",
            "specs": []
        }
    ],
    "lcname": "db-attribute"
}
        
Elapsed time: 4.52955s