Basic Decorator Operations

Creating a datalite class

A datalite class is a special dataclass. It is created by using a decorator @aiodatalite.datalite, members of this class are, from Python’s perspective, just normal classes. However, they have additional methods and attributes. @datalite decorator needs a database path to be provided. This database is the database the table for the dataclass will be created.

from aiodatalite import datalite
@datalite(db_path='db.db')
@dataclass
class Student:
    student_id: int = 1
    student_name: str = "Kurt Gödel"
    student_gpa: float = 3.9

Here, datalite will create a table called student in the database file db.db, this file will include all the fields of the dataclass as columns. Default value of these columns are same as the default value of the dataclass.

Special Methods

Each object initialised from a dataclass decorated with the @dataclass decorator automatically gains access to three special methods. It should be noted, due to the nature of the library, extensions such as mypy and IDEs such as PyCharm will not be able to see these methods and may raise exceptions. So, aiodatalite introduces typed module and DataliteHinted class, from which you can inherit your dataclass.

from aiodatalite import datalite
from aiodatalite.typed import DataliteHinted
@datalite(db_path='db.db')
@dataclass
class Student(DataliteHinted):
    student_id: int = 1
    student_name: str = "Kurt Gödel"
    student_gpa: float = 3.9

With this in mind, let us create a new object and run the methods over this objects.

new_student = Student(0, "Albert Einstein", 4.0)

Marking up Table

Due to the limitations of asynchronous programming, we cannot automatically create the table asynchronously, so we provide two ways to do this.

First way is to create the table automatically in !synchronous! mode by explicitly passing an argument to the decorator. We don’t know what consequences this can lead to specifically in your application, but if you are confident in yourself, use this method

...
@datalite(db_path='db.db', automarkup=True)
@dataclass
class Student(DataliteHinted):
...

The second way, which may be less convenient when using some frameworks but is more controllable, is to call the asynchronous create_table method, which will be added to your dataclass after using @datalite decorator, alongside with some other methods.

await new_student.markup_table()

Creating an Entry

First special method is .create_entry() when called on an object of a class decorated with the @datalite decorator, this method creates an entry in the table of the bound database of the class, in this case, table named student in the db.db. Therefore, to create the entry of Albert Einstein in the table:

await new_student.create_entry()

This also modifies the object in an intresting way, it adds a new attribute obj_id, this is a unique, autoincremented value in the database. It can be accessed by new_student.obj_id.

Updating an Entry

Second special method is .update_entry(). If an object’s attribute is changed, to update its record in the database, this method must be called.

new_student.student_gpa = 5.0  # He is Einstein, after all.
await new_student.update_entry()

Deleting an Entry

To delete an entry from the record, the third and last special method, .remove_entry() should be used.

await new_student.remove_entry()

Warning

It should be noted that, if the new_student.obj_id attribute is modified, .update_entry() and .remove_entry() may have unexpected results.

Tweaked Types

Sometimes your objects may be somewhat complex or use a nested structure. This fork allows nesting by using the pickle module, which gives you the ability to turn your objects into pure bytes that can be written to and from the database. When the tweaked parameter is enabled, data that can be written natively is written as is, and data that cannot be written in this way is first processed by pickle.

from aiodatalite import datalite
from aiodatalite.typed import DataliteHinted

# Bag dataclass defined somewhere

@datalite(db_path='db.db')
@dataclass
class Student(DataliteHinted):
    student_id: int = 1
    student_name: str = "Kurt Gödel"
    student_gpa: float = 3.9
    bag: Bag = Bag(
        size="big",
        items_number=88,
        ...
    )

But with great opportunity comes great responsibility, so using nested models can lead to difficulties in migrating data and updating the structure of nested objects, so changing the Bag object in this example, in theory, can break the database without the possibility of migration. We recommend using multiple tables and simple relationships between them (must be organized independently) in such cases.

The tweaked functionality of types can be disabled by passing the tweaked parameter as False to the datalite decorator

from aiodatalite import datalite
from aiodatalite.typed import DataliteHinted

# Bag is defined somewhere as datalite table instance

@datalite(db_path='db.db', tweaked=False)
@dataclass
class Student(DataliteHinted):
    bag_id: int
    student_id: int = 1
    student_name: str = "Kurt Gödel"
    student_gpa: float = 3.9