Cute sqlite3 wrapper for sql tables
Project description
SqlEngine
My SqlEngine is a cute little wrapper for sqlite3 table manipulations without any third party dependencies.
Home Page | Installation | Quick Start | Documentation
Features
SqlEngine abstracts SQL queries into tiny little methods like insert, insert_many, upsert, and not so tiny (but still cute and little) query builders like select, delete and update. SqlEngine also provides bulk insertion with insert_many and transaction operations like select.fetchmany_iterator. Methods can be executed either in transaction mode (thanks to transaction context manager) or right on the spot.
SqlEngine implements Jupyter integration and dynamic schema building. You can easily instantiate existing database table and view it in a cute little html representation.
from sqlengine import schema
table = schema.table_from_database("temp/chinook.db", "Album")
table
| AlbumId | Title | ArtistId |
| 1 | For Those About To Rock We Salute You | 1 |
| 2 | Balls to the Wall | 2 |
| 3 | Restless and Wild | 2 |
| 4 | Let There Be Rock | 1 |
| 5 | Big Ones | 3 |
| 6 | Jagged Little Pill | 4 |
| 7 | Facelift | 5 |
| 8 | Warner 25 Anos | 6 |
| 9 | Plays Metallica By Four Cellos | 7 |
| 10 | Audioslave | 8 |
| ... more rows ... | ||
Note that GitHub's html rendering is simplified.
You can preview select statements before fetching data to your variables.
table = schema.table_from_database("temp/chinook.db", "Invoice")
table.select("InvoiceId", "CustomerId", "BillingAddress", "BillingCountry", "Total")\
.where\
.gte("Total", 2.0)\
.then\
.order_by("CustomerId")\
.limit(10)
| InvoiceId | CustomerId | BillingAddress | BillingCountry | Total |
| 98 | 1 | Av. Brigadeiro Faria Lima, 2170 | Brazil | 3.98 |
| 121 | 1 | Av. Brigadeiro Faria Lima, 2170 | Brazil | 3.96 |
| 143 | 1 | Av. Brigadeiro Faria Lima, 2170 | Brazil | 5.94 |
| 327 | 1 | Av. Brigadeiro Faria Lima, 2170 | Brazil | 13.86 |
| 382 | 1 | Av. Brigadeiro Faria Lima, 2170 | Brazil | 8.91 |
| 12 | 2 | Theodor-Heuss-Straße 34 | Germany | 13.86 |
| 67 | 2 | Theodor-Heuss-Straße 34 | Germany | 8.91 |
| 219 | 2 | Theodor-Heuss-Straße 34 | Germany | 3.96 |
| 241 | 2 | Theodor-Heuss-Straße 34 | Germany | 5.94 |
| 99 | 3 | 1498 rue Bélanger | Canada | 3.98 |
Purpose
It's a tiny little modern ORM-like that lets you prototype your databases locally with great flexibility. Also, it can be used in production apps to store and retrieve data, because all select, update, delete queries are parametrized. But it does not restrict you from using your own queries which might not be parameterized with methods like select.custom() and where.custom(). Flexibility is a go to for this library.
And last (but not least) is data inspection. If you need to quickly inspect existing .db file but don't want to install yet another heavy ORM with a lot of unused dependencies and features, you might look into SqlEngine, as it uses only native python modules, implements dynamic schema builder and has a good synergy with Jupyter Notebook.
Installation
To install SqlEngine, you can use pip:
pip install sqlengine-lite
Env
You can set environment variable for logging. By default it's WARNING.
SQL_ENGINE_LOG_LEVEL=INFO
Quick Start
Here lays everything you need to know to start working with SqlEngine. For detailed usage, API reference, and advanced examples, see the full documentation.
Table Declaration
All you have to do to create your own cute little table is to inherit SqlTableMixin class or to create your own schema and declare desired types of your table's columns.
from sqlengine import SqlTableMixin, Primary
class Employees(SqlTableMixin):
ID : Primary[int]
Name : str | None
Occupation : str
Salary : float
More details at Declaration.
Instantiation
Create an instance of the table class with provided path to create or connect to. force_drop=True to overwrite existing table if it exists.
table = Employees("temp/data.db", force_drop=True)
More details at Instantiation.
Row insertion
Insert one row in a *args style.
table.insert(1, "John Doe", "Software Engineer", 75000.0)
Use kwargs mapping to insert/upsert one row
table.insert(2, salary=80000.0, name="Jane Smith", occupation="Data Scientist")
Bulk insert multiple rows.
employees_data = [
(3, "Alice Johnson", "Product Manager", 90000.0),
(4, "Bob Brown", "Project Manager", 78000.0),
(5, "Charlie Davis", "UI/UX Designer", 65000.0),
(6, "David Wilson", "DevOps Engineer", 82000.0),
(7, "Eve Taylor", "Customer Support", 45000.0),
(8, "Frank White", "Quality Assurance", 53000.0),
(9, "Grace Hall", "Marketing Manager", 68000.0),
(10, "Henry Lee", "Technical Writer", 52000.0)
]
table.insert_many(employees_data)
Upsert one row.
table.upsert(1, "Jane Doe", "Data Scientist", 80000.0)
Jupyter view
Inspect table in Jupyter Notebook.
table
| ID | Name | Occupation | Salary |
| 1 | Jane Doe | Data Scientist | 80000.0 |
| 2 | Jane Smith | Data Scientist | 80000.0 |
| 3 | Alice Johnson | Product Manager | 90000.0 |
| 4 | Bob Brown | Project Manager | 78000.0 |
| 5 | Charlie Davis | UI/UX Designer | 65000.0 |
| 6 | David Wilson | DevOps Engineer | 82000.0 |
| 7 | Eve Taylor | Customer Support | 45000.0 |
| 8 | Frank White | Quality Assurance | 53000.0 |
| 9 | Grace Hall | Marketing Manager | 68000.0 |
| 10 | Henry Lee | Technical Writer | 52000.0 |
Select Query
Helper constants for column names.
ID = "ID"
Name = "Name"
Occupation = "Occupation"
Salary = "Salary"
Query select and fetch in one go.
table.select.where.between(ID, 3, 5).then.fetchall()
# ->
# [(3, 'Alice Johnson', 'Product Manager', 90000.0),
# (4, 'Bob Brown', 'Project Manager', 78000.0),
# (5, 'Charlie Davis', 'UI/UX Designer', 65000.0)]
Iterate over select statements.
with table.transaction():
for id, occupation in table.select(ID, Occupation).where.gt(Salary, 70_000):
print(id, occupation)
1 Data Scientist
3 Product Manager
4 Project Manager
6 DevOps Engineer
Inspect select query in Jupyter.
table.select(Name, Salary).where.lt(Salary, 70_000)
| Name | Salary |
| Charlie Davis | 65000.0 |
| Eve Taylor | 45000.0 |
| Frank White | 53000.0 |
| Grace Hall | 68000.0 |
| Henry Lee | 52000.0 |
Update Query
# equal to ...update.set(Salary, 50_000)...
table.update(Salary, 50_000).where.eq(Name, "Eve Taylor").then.execute()
Delete Query
table.delete.where.eq(ID, 5).then.execute()
More details at Statements.
Transaction
Operate within a transaction
with table.transaction():
for row in employees_data:
table.upsert(*row)
More details at Transaction.
Get Item
Fetch row by primary key.
table[9] # -> (9, 'Grace Hall', 'Marketing Manager', 68000.0)
Fetch slice by integer primary key.
table[4:10:2]
# ->
# [(4, 'Bob Brown', 'Project Manager', 78000.0),
# (6, 'David Wilson', 'DevOps Engineer', 82000.0),
# (8, 'Frank White', 'Quality Assurance', 53000.0)]
More details at Syntax Sugar.
Custom Types
Declare your own non-native SQL type to be compatible with sqlite3.
from datetime import datetime
from sqlengine import SqlTableMixin, Primary
class DateTime(datetime):
def to_sql(self) -> str:
return self.strftime("%Y-%m-%d %H:%M")
@classmethod
def from_sql(cls, sql : bytes):
return cls.fromisoformat(sql.decode('utf-8'))
def __repr__(self):
return f"DateTime({self.time})"
class ReservationIndex(SqlTableMixin):
user_id : Primary[int]
room_id : Primary[str]
time_at : Primary[DateTime]
user_name : str | None
table = ReservationIndex("temp/data.db")
table.insert(
user_id=1,
room_id="loft_1",
time_at=DateTime.now()
)
table
| user_id | room_id | time_at | user_name |
| 1 | loft_1 | 2026-05-29 21:29:00 | None |
More details at Custom Types.
Pure SQL
Use SQL queries directly.
with table.transaction(autocommit=False):
table.conn.execute("DELETE FROM ReservationIndex WHERE user_id = 1;")
table.commit()
Same as:
table.conn.execute("DELETE FROM ReservationIndex WHERE user_id = 1;")
table
| user_id | room_id | time_at | user_name |
Csv Converter
Save table to csv.
from sqlengine.utils import to_csv
to_csv(table, "temp/table.csv")
Save query result to csv.
to_csv(table.select.where.gt(Salary, 70_000), "temp/query.csv")
Stream big tables or query results to csv.
with table.transaction():
to_csv(table, "temp/query.csv", stream_batch_size=1000)
Pandas-like Converter
Convert tables or query results to pandas DataFrame in one shot.
import pandas as pd
from sqlengine.utils import to_dicts
df = pd.DataFrame(to_dicts(table))
df.set_index("ID", inplace=True)
Or stream them via generator.
import pandas as pd
from sqlengine.utils import to_dicts_stream
df = pd.DataFrame(columns=table.columns)
with table.transaction():
for batch in to_dicts_stream(table, batch_size=1000):
df = pd.concat([df, pd.DataFrame(batch)], axis=0)
df.set_index("ID", inplace=True)
Contributions
Your impact is welcome. Install module from source if you want to contribute:
git clone https://github.com/suffermuffin/SQL-Engine.git
cd SQL-Engine
Use uv to sync dependencies and checkout to your new branch:
uv sync
git checkout -b "<your_feature_or_fix_name>"
Don't forget to run tests after the implementation:
uv run python -m unittest discover -s tests
And update api documentation with your docstrings:
pydoc-markdown
License
This project is licensed under the terms of the MIT license.
Project details
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 sqlengine_lite-2.2.2.tar.gz.
File metadata
- Download URL: sqlengine_lite-2.2.2.tar.gz
- Upload date:
- Size: 31.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8e3e0048fe2adca94c2fb2fa0fa0f2bd909c0e844f33398d6c4aa1883b557f9
|
|
| MD5 |
a186f46ebf2b4aa8400b5f5c3732f5f4
|
|
| BLAKE2b-256 |
c809c411cf14e14fe73e532e065c6b7c2d5a1f5bfac1446d91e12bd90b2b5b74
|
Provenance
The following attestation bundles were made for sqlengine_lite-2.2.2.tar.gz:
Publisher:
publish.yml on suffermuffin/SQL-Engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqlengine_lite-2.2.2.tar.gz -
Subject digest:
c8e3e0048fe2adca94c2fb2fa0fa0f2bd909c0e844f33398d6c4aa1883b557f9 - Sigstore transparency entry: 1705804131
- Sigstore integration time:
-
Permalink:
suffermuffin/SQL-Engine@6ec8f4a621404edce41f97f45ec4c6d398e40421 -
Branch / Tag:
refs/tags/v2.2.2 - Owner: https://github.com/suffermuffin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6ec8f4a621404edce41f97f45ec4c6d398e40421 -
Trigger Event:
release
-
Statement type:
File details
Details for the file sqlengine_lite-2.2.2-py3-none-any.whl.
File metadata
- Download URL: sqlengine_lite-2.2.2-py3-none-any.whl
- Upload date:
- Size: 24.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0199bece0572d389016823a0bfe78afc46d181aa6036644b249ca70bf129d890
|
|
| MD5 |
ac822bacdb40b7fdec084ecd9affab88
|
|
| BLAKE2b-256 |
7dad8aacea86f57d0a5e2ae944b0e4c6debc3029e0f0364106e6bd78372fb4d8
|
Provenance
The following attestation bundles were made for sqlengine_lite-2.2.2-py3-none-any.whl:
Publisher:
publish.yml on suffermuffin/SQL-Engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqlengine_lite-2.2.2-py3-none-any.whl -
Subject digest:
0199bece0572d389016823a0bfe78afc46d181aa6036644b249ca70bf129d890 - Sigstore transparency entry: 1705804172
- Sigstore integration time:
-
Permalink:
suffermuffin/SQL-Engine@6ec8f4a621404edce41f97f45ec4c6d398e40421 -
Branch / Tag:
refs/tags/v2.2.2 - Owner: https://github.com/suffermuffin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6ec8f4a621404edce41f97f45ec4c6d398e40421 -
Trigger Event:
release
-
Statement type: