Files
aiodatalite/docs/decorator.rst
2024-03-17 17:28:17 +02:00

166 lines
5.7 KiB
ReStructuredText

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.
.. code-block:: python
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.
.. code-block:: python
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.
.. code-block:: python
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
.. code-block:: python
...
@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.
.. code-block:: python
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:
.. code-block:: python
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.
.. code-block:: python
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.
.. code-block:: python
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.
.. code-block:: python
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
.. code-block:: python
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