Welcome to datalite3’s documentation!¶
Datalite is a simple to use Python library that can bind a dataclass to an sqlite3 database.
Documentation¶
Getting Started¶
Welcome to the documentation of datalite3
. datalite3
provides a simple, intuitive way to bind
dataclasses to sqlite3 databases. In its current version, it provides implicit support for
conversion between int
, float
, str
, bytes
classes and their sqlite3
counterparts, default values, basic schema migration and fetching functions.
Installation¶
Simply write:
pip install datalite3
In the shell. And then, whenever you want to use it in Python, you can use:
import datalite3
Basic Decorator Operations¶
Creating a datalite class¶
A datalite class is a special dataclass. It is created by using a decorator @datalite3.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 to be provided.
This is the database the table for the dataclass will be created in.
from datalite3 import datalite
@datalite('mydb.db')
@dataclass
class Student:
student_id: int = 1
student_name: str = "John Smith"
student_gpa: float = 3.9
Here, datalite
will create a table called student
in the database file mydb.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.
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)
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 mydb.db
. Therefore, to create the entry of Albert Einstein
in the table:
new_student.create_entry()
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
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.
new_student.remove_entry()
Constraints¶
One of the most useful features provided by SQLite is the concept of constraints. Constraints signal the SQL engine that the values hold in a specific column MUST abide by specific constraints, these might be
Values of this column cannot be
NULL
. (NOT NULL
)Values of this column cannot be repeated. (
UNIQUE
)Values of this column must fulfill a condition. (
CHECK
)Values of this column can be used to identify a record. (
PRIMARY
)Values of this column has a default value. (
DEFAULT
)
Some of these constraints are already implemented in datalite. With all of the set, is planned to be implemented in the future.
Default Values¶
Columns can be given default values. This is done the same way you would give a datafield a default value.
@datalite("mydb.db")
@dataclass
class Student:
student_id: int
name: str = "Albert Einstein"
Therefore, from now on, any Student
object, whose name is not specified, will
have the default name "Albert Einstein"
and if .create_entry()
method is
called on them, the newly inserted record will, by default, have this value in its
corresponding column.
Unique Values¶
Declaring a field unique is done by a special TypeVar
called Unique
this uniqueness check is done in the database level, this introduces has some pros,
but also some cons.
Pushing the uniqueness check to the database level introduces a better ability to
handle concurrency for applications with large traffic, however, this check only
runs when an item is registered, which means no problem will raise in
the object creation even if another object of the same type with the same value
hold in the unique field exists, no error will raise. However, if another record
with the same value in the unique field is recorded in the bound database, upon
the invocation of the .create_entry()
will raise the ConstraintFailedError
exception.
Uniqueness constraint is declared thusly:
from datalite3 import Unique
@datalite("mydb.db")
@dataclass
class Student:
student_id: Unique[int]
name: str = "Albert Einstein"
Hinting a field with the Unique[T]
type variable will introduce two rules:
The values in the column of the field in the table that represents the dataclass
Student
in the bound databasemydb.db
cannot be NULL, thus the corresponding field cannot be assignedNone
.These same values must be unique for each and every record.
Failure of any of these two rules will result in a ConstraintFailedError
exception.
Primary Key¶
In SQL, the PRIMARY KEY constraint uniquely identifies each record in a table. Primary keys must contain UNIQUE values, and cannot contain NULL values. A table can have only ONE primary key; and in the table, this primary key can consist of single or multiple columns (fields).
Declaring a field as part of the primary key is done by a special TypeVar
called Primary
.
For example, in the example of our Student
class, the primary key can be the field
student_id
, given that a student ID usually uniquely identifies a student within a given
institution.
from datalite3 import Primary
@datalite("mydb.db")
@dataclass
class Student:
student_id: Primary[int]
name: str = "Albert Einstein"
Hinting a field with the Primary[T]
type variable will introduce two rules:
The values in a
Primary
column cannot be NULL, thus the corresponding field cannot be assignedNone
.There cannot be two distinct records in the dataset with the same set of values in the primary key fields.
Failure of any of these two rules will result in a ConstraintFailedError
exception.
Fetching Functions¶
A database is hardly useful if data does not persist between program runs. In datalite3
one can use datalite3.fetch
module to fetch data back from the database.
There are different sorts of fetching. One can fetch all the objects of a class
using fetch_all(class_)
, or an object with a specific key using fetch_from(class_, key)
.
There are more functions for plural conditional fetching (fetch_if
, fetch_where
) where
all objects fitting a condition will be returned, as well as singular conditional fetching that returns
the first object that fits a condition (fetch_equals
).
Pagination¶
Pagination is a feature that allows a portion of the results to be returned. Since datalite3
is built to work with databases that may include large amounts of data, many systems using large
portions of data also make use of pagination. By building pagination inside the system, we hope to
allow easier usage.
fetch_where
fetch_if
fetch_all
Supports pagination, in general, pagination is controlled via two arguments page
and element_count
,
page
argument specifies which page to be returned and element_count
specifies how many elements
each page has. When page
is set to 0, all results are returned irregardless of the value of the
element_count
.
Important
More information regarding the datalite3.fetch
functions can be found in the API reference.
Schema Migrations¶
Datalite provides a module, datalite3.migrations
that handles schema migrations. When a class
definition is modified, datalite3.migrations.basic_migration
can be called to automatically
transfer records to a table fitting the new definitions.
Let us say we have made changes to the fields of a dataclass called Student
and now,
we want these changes to be made to the database. More specifically, we had a field called
studentt_id
and realised this was a typo, we want it to be named into student_id
,
and we want the values that was previously hold in this column to be persistent despite the
name change. We can achieve this easily by:
datalite3.basic_migration(Student, {'studentt_id': 'student_id'})
This will make all the changes, if we had not provided the second argument, the values would be lost.
API Reference¶
datalite3 Module¶
-
@
datalite3.
datalite
(db: Union[str, sqlite3.Connection], table_name: Optional[str] = None, *, auto_commit: bool = False, type_overload: Optional[Dict[type, datalite3.commons.SQLType]] = None) → Type[T]¶ Bind a dataclass to a sqlite3 database. This adds new methods to the class, such as create_entry(), remove_entry() and update_entry().
- Parameters
db – Path of the database to be binded.
table_name – Optional name for the table. The name of the class will be used by default.
auto_commit – Enable auto-commit.
type_overload – Type overload dictionary.
- Returns
The new dataclass.
datalite3.constraints module¶
- datalite3.constraints module introduces constraint
types that can be used to hint field variables, that can be used to signal datalite decorator constraints in the database.
-
exception
datalite3.constraints.
ConstraintFailedError
¶ Bases:
Exception
This exception is raised when a Constraint fails.
-
datalite3.constraints.
Unique
¶ - Dataclass fields hinted with this type signals
datalite that the bound column of this field in the table is part of the PRIMARY KEY.
alias of Union[T, Tuple[T]]
datalite3.fetch module¶
-
datalite3.fetch.
fetch_all
(class_: type, page: int = 0, element_count: int = 10) → tuple¶ Fetchall the records in the bound database.
- Parameters
class – Class of the records.
page – Which page to retrieve, default all. (0 means closed).
element_count – Element count in each page.
- Returns
All the records of type class_ in the bound database as a tuple.
-
datalite3.fetch.
fetch_equals
(class_: type, field: str, value: Any) → Any¶ Fetch a class_ type variable from its bound db.
- Parameters
class – Class to fetch.
field – Field to check for, by default, object id.
value – Value of the field to check for.
- Returns
The object whose data is taken from the database.
-
datalite3.fetch.
fetch_from
(class_: type, key: Union[None, int, float, str, bytes, Tuple[Union[None, int, float, str, bytes]]]) → Any¶ Fetch a class_ type variable from its bound dv.
- Parameters
class – Class to fetch from.
key – Unique key of the object.
- Returns
The fetched object.
-
datalite3.fetch.
fetch_if
(class_: type, condition: str, page: int = 0, element_count: int = 10) → tuple¶ Fetch all class_ type variables from the bound db, provided they fit the given condition
- Parameters
class – Class type to fetch.
condition – Condition to check for.
page – Which page to retrieve, default all. (0 means closed).
element_count – Element count in each page.
- Returns
A tuple of records that fit the given condition of given type class_.
-
datalite3.fetch.
fetch_where
(class_: type, field: str, value: Any, page: int = 0, element_count: int = 10) → tuple¶ Fetch all class_ type variables from the bound db, provided that the field of the records fit the given value.
- Parameters
class – Class of the records.
field – Field to check.
value – Value to check for.
page – Which page to retrieve, default all. (0 means closed).
element_count – Element count in each page.
- Returns
A tuple of the records.
-
datalite3.fetch.
is_fetchable
(class_: type, key: Union[None, int, float, str, bytes, Tuple[Union[None, int, float, str, bytes]]]) → bool¶ Check if a record is fetchable given its key and class_ type.
- Parameters
class – Class type of the object.
key – Unique key of the object.
- Returns
If the object is fetchable.
datalite3.mass_actions module¶
This module includes functions to insert multiple records to a bound database at one time, with one time open and closing of the database file.
-
exception
datalite3.mass_actions.
HeterogeneousCollectionError
¶ Bases:
Exception
- :raiseif the passed collection is not homogeneous.
ie: If a List or Tuple has elements of multiple types.
-
datalite3.mass_actions.
create_many
(objects: Union[List[T], Tuple[T]]) → None¶ Insert many records corresponding to objects in a tuple or a list.
- Parameters
objects – A tuple or a list of objects decorated with datalite.
- Returns
None.
datalite3.migrations module¶
Migrations module deals with migrating data when the object definitions change. This functions deal with Schema Migrations.
-
datalite3.migrations.
basic_migrate
(class_: Type[dataclasses.dataclass], column_transfer: dict = None) → None¶ Given a class, compare its previous table, delete the fields that no longer exist, create new columns for new fields. If the column_flow parameter is given, migrate elements from previous column to the new ones.
- Parameters
class – Datalite class to migrate.
column_transfer – A dictionary showing which columns will be copied to new ones.
- Returns
None.