deev (דיב) is an entity framework for Python.
This README is only a high-level introduction to deev. For more detailed documentation, please view the official docs at https://deev.readthedocs.io.
- Entity-based CRUD — work with Python objects, not hand-crafted SQL.
- Validation — entities are validated before persistence and on-demand.
- Transaction Contexts — scope transactions with context managers, never leave a mismanaged state.
- Migrations — version-controlled schema and data migrations via the
db-migrateCLI or programmatically via theDbMigrateclass. - Multi-provider — built-in providers for SQLite, MySQL, ClickHouse, and MongoDB.
- PEP 249 compatibility — switch DBMS without refactoring data access code.
- Parameterized queries — unified
%?syntax across all providers. - Connection strings — DSN URIs (
sqlite3:///,mysql://) and OLEDB-style strings. - Declarative indexes — define composite, unique, and directional indexes on entity fields.
- Native Interface Access — drop down to provider-specific methods when the ORM layer isn't enough.
Installation
You can install deev from PyPI through usual means, such as pip:
pip install deev
Usage
Let's have a look at the two popular use cases: using Python objects for CRUD operations, and using the db-migrate CLI tool to manage DB schema.
Entity CRUD
First, let's define a "SimpleEntity" class we will use as a database entity:
from datetime import datetime, timezone
from deev import entity, field
from typing import Optional
# ./SimpleEntity.py
@entity
class SimpleEntity:
column1: int
column2: Optional[list[str]] = field(default=None)
column3: Optional[datetime] = field(default=lambda: datetime.now(timezone.utc))
id: int = field(autoincrement=True, default=None, primary_key=True)
Next, let's write some CRUD-based code:
# imports
from deev import entity, field
# define a simple entity with an auto-increment PK, an int value column, and a list[str] column
@entity
class SimpleEntity:
id: int = field(autoincrement=True, primary_key=True)
column1: int
column2: list[str]
# create a database using familiar connection-string syntax
from deev.utils import create_database
connection_str = 'Server=./test_data/;Database=sqlite3/test.db;Provider=sqlite3'
create_database(connection_str)
# connect to your database, create a table for storage, and perform some CRUD operations
from deev import connect
from deev.sqlite import SqliteTableAdapter
with connect(connection_str) as db:
table = SqliteTableAdapter[SimpleEntity](db)
table.create_table()
# CREATE
entity_key = table.create(SimpleEntity(
column1=1,
column2=[3, 2, 1]
))
# READ
entity = table.read(**entity_key)
assert entity.id is not None
assert entity.column1 == 1
assert entity.column2[0] == 3
assert entity.column2[1] == 2
assert entity.column2[2] == 1
# UPDATE
entity.column2[1] = 4
table.update(entity)
# DELETE
table.delete(**entity_key)
# alternatives: upsert + query
entity_key = table.upsert(SimpleEntity(
column1=2,
column2=[5]
))
entity_key = table.upsert(SimpleEntity(
column1=2,
column2=[6]
))
results = table.query(
where='column1 = %?',
orderby='column1 DESC',
limit=2,
params=(2,)
)
count = 0
for result in results:
assert result.column2[0] in (5, 6)
count += 1
assert count == 2
# query kwargs are optional, for example this creates a generator for all table records:
results = table.query()
CLI db-migrate Tool
.. note:: For comprehensive migration documentation (provider-specific behavior, best practices, DDL auto-commit considerations), see the full Migration Guide.
The db-migrate tool can be used to apply a migration script or undo a previously applied migration script.
Basic syntax:
$ db-migrate -h
usage: db-migrate [-h] [--verbose] <COMMAND> ...
Utility for applying, undoing, or generating migrations.
positional arguments:
<COMMAND> Action to perform.
apply Apply migrations.
generate Generate DDL.
undo Undo migrations.
options:
-h, --help show this help message and exit
--verbose Enable verbose logging.
$ db-migrate apply -h
usage: db-migrate apply [-h] [--stop-at name] connectionstring [path]
positional arguments:
connectionstring Database connection string.
path Directory containing migration scripts (optional). If omitted, a path is calculated from the connectionstring argument, ie.
`./migrations/database_name/`.
options:
-h, --help show this help message and exit
--stop-at name Stop processing at the named migration (use "all" to process all).
:::note
The generate subcommand is alpha-quality and subject to change without notice. Do not rely on its interface for production tooling.
:::
A migration script is a Python file which defines two functions apply(...) and undo(...), each receiving a DbTransactionContext you can use to modify the database transactionally.
As an example, we will create two migration scripts "000_initial_schema.py" and "001_initial_seed.py", we name them so their sort order ensures the schema script runs before the seed script. (A practice used on internal projects is to use a datecode, issue number, or similar linearly progressing value.)
# ./migrations/test_db/000_initial_schema.py
from deev.common import DbTransactionContext
from deev.utils import create_table_adapter
from .SimpleEntity import SimpleEntity
def apply(transaction: DbTransactionContext) -> None:
table_adapter = create_table_adapter(SimpleEntity, transaction)
table_adapter.create_table()
transaction.commit()
def undo(transaction: DbTransactionContext) -> None:
transaction.execute_nonquery('DROP TABLE `SimpleEntities`;')
transaction.commit()
# ./migrations/test_db/001_initial_seed.py
from deev.common import DbTransactionContext
from deev.utils import create_table_adapter
from .SimpleEntity import SimpleEntity
def apply(transaction: DbTransactionContext) -> None:
table_adapter = create_table_adapter(SimpleEntity, transaction)
table_adapter.create(SimpleEntity(
column1 = 345
))
table_adapter.create(SimpleEntity(
column1 = 456
))
transaction.commit()
def undo(transaction: DbTransactionContext) -> None:
transaction.execute_nonquery('DELETE FROM `SimpleEntities` WHERE `column1` IN (345, 456)')
transaction.commit()
Finally, we can apply the change to our existing database:
# apply schema change
db-migrate apply 'Server=./test_data/;Database=sqlite3/test.db;Provider=sqlite3' ./migrations/test_db/
..apply migration "000_initial_schema"
..apply migration "001_initial_seed"
Migrations applied 2, skipped 0, available 2.
We can also undo the change after it has been applied:
# undo schema change
db-migrate undo 'Server=./test_data/;Database=sqlite3/test.db;Provider=sqlite3' ./migrations/test_db/
..undo migration "001_initial_seed"
..undo migration "000_initial_schema"
Migrations undone 2, skipped 0, available 2.
Contact
You can reach me on Discord or open an Issue on Github.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file deev-1.6.11.tar.gz.
File metadata
- Download URL: deev-1.6.11.tar.gz
- Upload date:
- Size: 96.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ddaea7081b6695300806adca5fbcce83169aca320c67b2db0da09602b4e0567
|
|
| MD5 |
f183dfb4625f802be5d3313a90050834
|
|
| BLAKE2b-256 |
35e42a6364289707aaba24f9d6e781bdc53863a2ef0ad380c7fb8d90d76bf764
|
File details
Details for the file deev-1.6.11-py3-none-any.whl.
File metadata
- Download URL: deev-1.6.11-py3-none-any.whl
- Upload date:
- Size: 136.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4af86d20c33f5fb58d64f2d985b4691bae03bb81f04557150c5e39b1a153c0c
|
|
| MD5 |
5521ccd321d6843f07903187f5ee734b
|
|
| BLAKE2b-256 |
9bbf533398710412503c3fbd6fda75fe9f41f8f48dec4df9321f211626c5f193
|