Async SQLAlchemy-based implementation of the fastlite API
Project description
DeeBase
Async SQLAlchemy-based database library with an ergonomic, fastlite-inspired API
DeeBase provides a simple, intuitive interface for async database operations in Python. Built on SQLAlchemy, it combines the ergonomics of fastlite with full async/await support and multi-database compatibility.
Features
- ๐ Async/Await - Built for modern async Python (FastAPI, etc.)
- ๐ Ergonomic API - Simple, intuitive database operations
- ๐ Type Safety - Optional dataclass support with IDE autocomplete
- ๐ฏ Multi-Database - SQLite and PostgreSQL support
- ๐ ๏ธ Rich Types - Text, JSON, datetime, Optional support
- โก Dynamic Access - Access tables with
db.t.tablename - ๐ Views Support - Read-only database views
- ๐จ Error Handling - 6 specific exception types with rich context
- ๐ค Code Generation - Export schemas as Python dataclasses
Quick Start
Installation
# Using uv (recommended)
uv add sqlalchemy aiosqlite
# Using pip
pip install sqlalchemy aiosqlite
Basic Example
from deebase import Database
from datetime import datetime
# Connect to database
db = Database("sqlite+aiosqlite:///myapp.db")
# Define schema
class User:
id: int
name: str
email: str
created_at: datetime
# Create table
users = await db.create(User, pk='id')
# Insert
user = await users.insert({
"name": "Alice",
"email": "alice@example.com",
"created_at": datetime.now()
})
# Returns: {'id': 1, 'name': 'Alice', 'email': 'alice@example.com', ...}
# Query
all_users = await users() # All records
user = await users[1] # By primary key
user = await users.lookup(email="alice@example.com") # By column
# Update
user['name'] = "Alice Smith"
await users.update(user)
# Delete
await users.delete(1)
await db.close()
Type Safety with Dataclasses
# Enable dataclass mode for type-safe operations
UserDC = users.dataclass()
# Now all operations return dataclass instances
user = await users[1]
print(user.name) # IDE autocomplete works!
print(user.email)
# Insert with dataclass
new_user = await users.insert(UserDC(
id=None,
name="Bob",
email="bob@example.com",
created_at=datetime.now()
))
Rich Type System
from deebase import Database, Text
from typing import Optional
from datetime import datetime
class Article:
id: int
title: str # VARCHAR (limited)
content: Text # TEXT (unlimited)
metadata: dict # JSON column
tags: Optional[list] # JSON, nullable
published: bool # BOOLEAN
view_count: int # INTEGER
created_at: datetime # TIMESTAMP
updated_at: Optional[datetime] # TIMESTAMP, nullable
articles = await db.create(Article, pk='id')
article = await articles.insert({
"title": "Getting Started",
"content": "A very long article...",
"metadata": {"author": "Alice", "category": "tutorial"},
"tags": ["python", "async"],
"published": True,
"view_count": 0,
"created_at": datetime.now()
})
Error Handling
DeeBase provides specific exception types with rich context:
from deebase import NotFoundError, IntegrityError, ValidationError
try:
user = await users[999]
except NotFoundError as e:
print(f"Not found in {e.table_name}")
print(f"Filters: {e.filters}")
try:
await users.insert({"email": "duplicate@example.com"})
except IntegrityError as e:
print(f"Constraint {e.constraint} violated")
try:
await users.update({"name": "Missing PK"}) # No ID
except ValidationError as e:
print(f"Invalid {e.field}: {e.value}")
Working with Existing Databases
# Connect to existing database
db = Database("sqlite+aiosqlite:///existing.db")
# Reflect all tables
await db.reflect()
# Access tables
users = db.t.users
posts = db.t.posts
# CRUD operations work normally
user = await users[1]
all_posts = await posts()
Database Views
# Create view
popular_posts = await db.create_view(
"popular_posts",
"SELECT * FROM posts WHERE views > 1000"
)
# Query view (read-only)
posts = await popular_posts()
# Access via db.v
view = db.v.popular_posts
Filtering with xtra()
# Create filtered view of table
admin_users = users.xtra(role="admin")
active_admins = admin_users.xtra(active=True)
# All operations respect filters
admins = await active_admins()
# Insert automatically sets filter values
await active_admins.insert({"name": "Eve", "email": "eve@example.com"})
# Automatically sets role='admin' and active=True
Code Generation
Export your database schema as Python dataclasses:
from deebase import create_mod_from_tables
# Connect and reflect
db = Database("sqlite+aiosqlite:///myapp.db")
await db.reflect()
# Export all tables to models.py
create_mod_from_tables(
"models.py",
db.t.users,
db.t.posts,
db.t.comments,
overwrite=True
)
# Now you can:
# from models import User, Post, Comment
Examples
Runnable examples are available in the examples/ folder:
- phase1_raw_sql.py - Raw SQL queries
- phase2_table_creation.py - Creating tables from Python classes
- phase3_crud_operations.py - Full CRUD operations
- phase4_dataclass_support.py - Type-safe operations with dataclasses
- phase5_reflection.py - Working with existing databases
- phase7_views.py - Database views
- complete_example.py - Full-featured blog database
Run any example:
uv run examples/complete_example.py
Documentation
- API Reference - Complete API documentation
- Migration Guide - Migrating from fastlite
- Implementation Guide - Detailed feature guide
- How It Works - Technical deep dive
- Type Reference - Type system guide
Supported Databases
| Database | Status | Driver |
|---|---|---|
| SQLite | โ Fully tested | aiosqlite |
| PostgreSQL | ๐ง Infrastructure ready | asyncpg |
Supported Python Types
| Python Type | Database Type | Notes |
|---|---|---|
int |
INTEGER | |
str |
VARCHAR | Limited length |
Text |
TEXT | Unlimited length |
float |
REAL/FLOAT | |
bool |
BOOLEAN | 0/1 in SQLite |
bytes |
BLOB/BYTEA | |
dict |
JSON | Auto-serialized in SQLite |
datetime |
TIMESTAMP | |
date |
DATE | |
time |
TIME | |
Optional[T] |
NULL-able | Any type can be nullable |
Exception Types
| Exception | When Raised | Attributes |
|---|---|---|
NotFoundError |
Record not found | table_name, filters |
IntegrityError |
Constraint violation | constraint, table_name |
ValidationError |
Invalid input | field, value |
SchemaError |
Schema error | table_name, column_name |
ConnectionError |
Connection failed | database_url |
InvalidOperationError |
Invalid operation | operation, target |
FastAPI Integration
from fastapi import FastAPI, Depends, HTTPException
from deebase import Database, NotFoundError
app = FastAPI()
def get_db():
return Database("sqlite+aiosqlite:///myapp.db")
@app.get("/users")
async def list_users(db: Database = Depends(get_db)):
users = db.t.users
return await users()
@app.get("/users/{user_id}")
async def get_user(user_id: int, db: Database = Depends(get_db)):
try:
users = db.t.users
return await users[user_id]
except NotFoundError:
raise HTTPException(status_code=404, detail="User not found")
Comparison with fastlite
DeeBase replicates the fastlite API with async support:
| Feature | fastlite | DeeBase |
|---|---|---|
| Syntax | Synchronous | Async (requires await) |
| Backend | sqlite-utils | SQLAlchemy |
| Databases | SQLite only | SQLite + PostgreSQL |
| Type Safety | Optional dataclasses | Optional dataclasses |
| CRUD Operations | Yes | Yes |
| Views | Yes | Yes |
| Dynamic Access | db.t.tablename |
db.t.tablename (after reflection) |
| Error Handling | Basic | 6 specific exception types |
| Code Generation | No | Yes (create_mod()) |
See the Migration Guide for detailed comparison.
Development
Running Tests
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=src/deebase --cov-report=html
# Run specific test file
uv run pytest tests/test_crud.py -v
All 161 tests passing โ
Project Structure
deebase/
โโโ src/deebase/
โ โโโ __init__.py # Public API
โ โโโ database.py # Database class
โ โโโ table.py # Table operations
โ โโโ view.py # View support
โ โโโ column.py # Column access
โ โโโ types.py # Type mapping
โ โโโ dataclass_utils.py # Dataclass utilities
โ โโโ exceptions.py # Exception classes
โโโ tests/ # 161 passing tests
โโโ examples/ # Runnable examples
โโโ docs/ # Documentation
โโโ README.md # This file
Requirements
- Python 3.14+
- sqlalchemy 2.0.45+
- aiosqlite 0.22.0+
- greenlet 3.3.0+ (for SQLAlchemy async)
Design Philosophy
DeeBase follows these principles:
- Start Simple - Begin with dicts, opt-in to dataclasses for type safety
- Async First - All operations are async for modern frameworks
- Database Agnostic - Write once, run on SQLite or PostgreSQL
- No Magic - Transparent SQLAlchemy usage with escape hatches
- Production Ready - Comprehensive error handling and testing
Status
All 8 development phases complete! Ready for production use.
- โ Phase 1: Core Infrastructure
- โ Phase 2: Table Creation & Schema
- โ Phase 3: CRUD Operations
- โ Phase 4: Dataclass Support
- โ Phase 5: Dynamic Access & Reflection
- โ Phase 6: xtra() Filtering
- โ Phase 7: Views Support
- โ Phase 8: Polish & Utilities
See Implementation Plan for details.
Contributing
This project follows an 8-phase development plan (now complete). See docs/implementation_plan.md for the roadmap.
License
TBD
Acknowledgments
- Inspired by fastlite by Jeremy Howard
- Built on SQLAlchemy
- Async support via aiosqlite
Made with โค๏ธ for the async Python community
Project details
Release history Release notifications | RSS feed
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 deebase-0.1.0.tar.gz.
File metadata
- Download URL: deebase-0.1.0.tar.gz
- Upload date:
- Size: 18.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
87afed5b92e9bbdf57408a252c956d7948bfc253d3b36976ff616b65a96f307e
|
|
| MD5 |
444cba031e78acf5cddb63a19c6db29c
|
|
| BLAKE2b-256 |
18d986fd4b65c6839cd91c607c17f6b8accccb82d34987ca42787498060da7f2
|
File details
Details for the file deebase-0.1.0-py3-none-any.whl.
File metadata
- Download URL: deebase-0.1.0-py3-none-any.whl
- Upload date:
- Size: 21.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3e2a77a477332e31344b6df47b0e3e4fefb60554204242bce3c5f9a2c6aa621
|
|
| MD5 |
d32fa465663e8f54984093c53f48c0a8
|
|
| BLAKE2b-256 |
ebbb2b63ea7bfe9df70278ff863992ac87ce418e96de698496ed009b1f4f2c41
|