Skip to main content

Cute sqlite3 wrapper for sql tables

Project description

Tests Publishing PyPI

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
Album
AlbumIdTitleArtistId
1For Those About To Rock We Salute You1
2Balls to the Wall2
3Restless and Wild2
4Let There Be Rock1
5Big Ones3
6Jagged Little Pill4
7Facelift5
8Warner 25 Anos6
9Plays Metallica By Four Cellos7
10Audioslave8
... 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)
Invoice
InvoiceIdCustomerIdBillingAddressBillingCountryTotal
981Av. Brigadeiro Faria Lima, 2170Brazil3.98
1211Av. Brigadeiro Faria Lima, 2170Brazil3.96
1431Av. Brigadeiro Faria Lima, 2170Brazil5.94
3271Av. Brigadeiro Faria Lima, 2170Brazil13.86
3821Av. Brigadeiro Faria Lima, 2170Brazil8.91
122Theodor-Heuss-Straße 34Germany13.86
672Theodor-Heuss-Straße 34Germany8.91
2192Theodor-Heuss-Straße 34Germany3.96
2412Theodor-Heuss-Straße 34Germany5.94
9931498 rue BélangerCanada3.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
Employees
IDNameOccupationSalary
1Jane DoeData Scientist80000.0
2Jane SmithData Scientist80000.0
3Alice JohnsonProduct Manager90000.0
4Bob BrownProject Manager78000.0
5Charlie DavisUI/UX Designer65000.0
6David WilsonDevOps Engineer82000.0
7Eve TaylorCustomer Support45000.0
8Frank WhiteQuality Assurance53000.0
9Grace HallMarketing Manager68000.0
10Henry LeeTechnical Writer52000.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)
Employees
NameSalary
Charlie Davis65000.0
Eve Taylor45000.0
Frank White53000.0
Grace Hall68000.0
Henry Lee52000.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
ReservationIndex
user_idroom_idtime_atuser_name
1loft_12026-05-29 21:29:00None

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
ReservationIndex
user_idroom_idtime_atuser_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

sqlengine_lite-2.2.2.tar.gz (31.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sqlengine_lite-2.2.2-py3-none-any.whl (24.6 kB view details)

Uploaded Python 3

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

Hashes for sqlengine_lite-2.2.2.tar.gz
Algorithm Hash digest
SHA256 c8e3e0048fe2adca94c2fb2fa0fa0f2bd909c0e844f33398d6c4aa1883b557f9
MD5 a186f46ebf2b4aa8400b5f5c3732f5f4
BLAKE2b-256 c809c411cf14e14fe73e532e065c6b7c2d5a1f5bfac1446d91e12bd90b2b5b74

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlengine_lite-2.2.2.tar.gz:

Publisher: publish.yml on suffermuffin/SQL-Engine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for sqlengine_lite-2.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 0199bece0572d389016823a0bfe78afc46d181aa6036644b249ca70bf129d890
MD5 ac822bacdb40b7fdec084ecd9affab88
BLAKE2b-256 7dad8aacea86f57d0a5e2ae944b0e4c6debc3029e0f0364106e6bd78372fb4d8

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlengine_lite-2.2.2-py3-none-any.whl:

Publisher: publish.yml on suffermuffin/SQL-Engine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page