Lightweight and declarative wrapper over SQLAlchemy
Project description
sqla-lite
sqla-lite is a lightweight, declarative, and elegant wrapper over SQLAlchemy. It is designed to bring the joy and simplicity of frameworks like Java Spring Boot / JPA to Python, enabling you to define entities and repositories without the boilerplate of manually mapping declarative bases and session handlers.
Tired of using Mapped[int] = mapped_column(...) over and over? Welcome to sqla-lite.
Want to contribute? See CONTRIBUTING.md.
📦 Installation
Install directly from PyPI:
pip install sqla-lite
sqla-lite already brings its runtime dependency (SQLAlchemy >= 2.0.0).
🚀 Quick Start (The Basics)
Define your models exactly like you would writing pure Python data structures. Use the @table class decorator instead of dealing with explicit inheritance from DeclarativeBase.
You can annotate your attributes by assigning the markers (Id(), Size()) directly!
from sqla_lite import table, Id, Size
@table("users")
class User:
id: int = Id() # Automatically setup as Primary Key
name: str = Size(100) # VARCHAR(100)
age: int # Automatically inferred as Integer
Automatic Repositories
Say goodbye to the with Session(engine) as session: nightmare. With sqla-lite, you can register a repository to manage your Data Access layer globally for an entity!
from sqla_lite import repository, configure_database
# 1. Define an empty Repository pointing to your Entity
@repository(User)
class UserRepository:
pass
# 2. Configure your Database globally ONCE (Usually at the start of your application)
from sqlalchemy import create_engine
engine = create_engine("sqlite:///:memory:")
# sqla-lite will generate all tables automatically in Base.metadata (if needed)
from sqla_lite.core import Base
Base.metadata.create_all(engine)
# Inform sqla-lite to use this global engine!
configure_database(engine)
# 3. Use it! No sessions needed!
repo = UserRepository()
user = User(name="John Doe", age=25)
repo.save(user) # Auto-commits
# Search by primary key natively!
john = repo.get(1)
print(john.name)
⚡ Intermediate Usage
Type Inferences and Specific Mappings
sqla-lite understands your annotations and makes reasonable defaults.
strautomatically becomesString(256)if noSize()is provided.floatbecomesFloat.- Want highly-precise decimal numbers for currencies? Use the
Decimalmarker!
from sqla_lite import Decimal
@table("products")
class Product:
id: int = Id()
title: str = Size(150)
# 10 digits in total, 2 fractional decimal numbers -> Numeric(10, 2)
price: float = Decimal(precision=10, scale=2)
Date Handling
Handling Date strings and casting them into Database Datetime correctly can be a headache. sqla-lite supports both:
- Native Python formats: Using
datetime.datetimedirectly. - String Parsing Formats: Use the
DateFormatto transparently map python Strings into databaseDateTimeseamlessly!
import datetime
from sqla_lite import DateFormat
@table("events")
class Event:
id: int = Id()
# Kept as native Datetime everywhere
created_at: datetime.datetime
# Allows assigning strings in Python ("27/02/2026"). It'll be saved as a Datetime on DB!
completed_at: str = DateFormat("%d/%m/%Y")
Example:
evt = Event(
created_at=datetime.datetime.now(),
completed_at="27/02/2026"
)
repo.save(evt)
🌋 Advanced Usage
Composite Primary Keys
If your database design demands more complex structures like Many-To-Many resolution tables, or Legacy composite-keys, simply annotate multiple attributes with the Id() marker.
If one of the primary keys is a String and requires a size, you can pass the argument size= into the Id marker.
@table("employee_roles")
class EmployeeRole:
# Key 1
employee_id: int = Id()
# Key 2: String with length!
role_name: str = Id(size=50)
assigned_date: datetime.datetime
Querying with Repositories over Composite Keys
You don't need tuples or weird abstractions to retrieve composed key rows via our Repository Pattern. Just pass your identifiers in the sequence they were declared!
@repository(EmployeeRole)
class EmployeeRoleRepo: pass
repo = EmployeeRoleRepo()
# The Repository handles the argument unpacking dynamically
role = repo.get(101, "Software Engineer")
print(f"Loaded Role for Employee {role.employee_id}!")
Relationships (Foreign Keys)
sqla-lite now supports relationship markers for all common cases:
ManyToOne(many rows reference one parent)OneToOne(unique reference)OneToMany(list side of one-to-many)ManyToMany(list-to-list through association table)
ManyToOne with simple FK
from sqla_lite import table, Id, Size, Decimal, ManyToOne
@table("products")
class Product:
id: int = Id()
title: str = Size(150)
price: float = Decimal(precision=10, scale=2)
@table("stocks")
class Stock:
id: int = Id()
product: Product = ManyToOne(fields="id")
This creates stock.product_id as foreign key to products.id.
ManyToOne with composite FK
Use fields as comma-separated string or list:
from sqla_lite import table, Id, Size, ManyToOne
@table("companies")
class Company:
tenant_id: int = Id()
code: str = Id(size=20)
name: str = Size(100)
@table("employees")
class Employee:
id: int = Id()
company: Company = ManyToOne(fields=["tenant_id", "code"])
Equivalent form:
company: Company = ManyToOne(fields="tenant_id,code")
OneToOne
from sqla_lite import table, Id, Size, OneToOne
@table("profiles")
class Profile:
id: int = Id()
user_name: str = Size(80)
@table("profile_details")
class ProfileDetail:
id: int = Id()
profile: Profile = OneToOne(fields="id")
OneToOne applies a unique constraint on the generated FK columns.
OneToMany
from sqla_lite import table, Id, Size, ManyToOne, OneToMany
@table("parents")
class Parent:
id: int = Id()
name: str = Size(80)
children: list["Child"] = OneToMany(mapped_by="parent")
@table("children")
class Child:
id: int = Id()
parent: Parent = ManyToOne(fields="id", back_populates="children")
title: str = Size(120)
ManyToMany
from sqla_lite import table, Id, Size, ManyToMany
@table("permissions")
class Permission:
id: int = Id()
name: str = Size(60)
@table("users")
class User:
id: int = Id()
user_name: str = Size(80)
permissions: list[Permission] = ManyToMany()
An association table is generated automatically.
🔥 Extending Repositories
Because your Repository is a plain Python Class wrapped by @repository, you can implement custom behavior that fits your business logic inside of it. The decorator only injects basic (save, get, delete, find_all) methods, leaving you free to query anything else you like via self.engine:
from sqlalchemy.orm import Session
@repository(User)
class UserRepository:
def find_adults(self):
with Session(self.engine) as session:
# self.entity_class holds a reference to the mapped Class!
return session.query(self.entity_class).filter(self.entity_class.age >= 18).all()
# Usage:
repo = UserRepository()
adults = repo.find_adults()
Created with ❤️. Say goodbye to boilerplate code!
Support this project on Patreon: https://www.patreon.com/cw/ElaraDevSolutions
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 sqla_lite-1.0.6.tar.gz.
File metadata
- Download URL: sqla_lite-1.0.6.tar.gz
- Upload date:
- Size: 18.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
913ec5f16331146d4ba622d636b6c580cc81f906df3bd4efd761c42079e576e7
|
|
| MD5 |
2f4117d36829e5427c95a4f859d9daf7
|
|
| BLAKE2b-256 |
5f3e43727638bf040922804598d964dfcb6742a7140f1d6c9b2f971a9548c5f1
|
Provenance
The following attestation bundles were made for sqla_lite-1.0.6.tar.gz:
Publisher:
publish-pypi.yml on ElaraDevSolutions/sqla-lite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqla_lite-1.0.6.tar.gz -
Subject digest:
913ec5f16331146d4ba622d636b6c580cc81f906df3bd4efd761c42079e576e7 - Sigstore transparency entry: 1005610862
- Sigstore integration time:
-
Permalink:
ElaraDevSolutions/sqla-lite@14869e38fc5e474d5e84137dba0666d5de6666a9 -
Branch / Tag:
refs/tags/v1.0.6 - Owner: https://github.com/ElaraDevSolutions
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@14869e38fc5e474d5e84137dba0666d5de6666a9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sqla_lite-1.0.6-py3-none-any.whl.
File metadata
- Download URL: sqla_lite-1.0.6-py3-none-any.whl
- Upload date:
- Size: 13.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f00954092d8947b8c88fc2ae06fd7b335e714c718c46a6796b2a657c3bc5369
|
|
| MD5 |
678f602a8e59bac3bf445862b5fc956f
|
|
| BLAKE2b-256 |
0bdc32d21107b7242ecb9a8dfd94cdf6ffa433decfae182eb4e0dfed188e7592
|
Provenance
The following attestation bundles were made for sqla_lite-1.0.6-py3-none-any.whl:
Publisher:
publish-pypi.yml on ElaraDevSolutions/sqla-lite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqla_lite-1.0.6-py3-none-any.whl -
Subject digest:
3f00954092d8947b8c88fc2ae06fd7b335e714c718c46a6796b2a657c3bc5369 - Sigstore transparency entry: 1005610863
- Sigstore integration time:
-
Permalink:
ElaraDevSolutions/sqla-lite@14869e38fc5e474d5e84137dba0666d5de6666a9 -
Branch / Tag:
refs/tags/v1.0.6 - Owner: https://github.com/ElaraDevSolutions
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@14869e38fc5e474d5e84137dba0666d5de6666a9 -
Trigger Event:
push
-
Statement type: