๐Ÿ“˜ ModelAPI โ€“ A Friendly Guide to a Lightweight SQLite ORM๏ƒ

Note: ModelAPI uses a different structure than TableAPI! So if youโ€™re coming from that, treat this as a fresh start.


๐Ÿ“š Table of Contents๏ƒ


๐Ÿง  Introduction๏ƒ

ModelAPI gives you a Laravel-style, class-based ORM interface for SQLite in Python. Itโ€™s minimal, fast, and easy to use.

Think of it like this:

  • You define your data with Python classes.

  • The ORM handles schema definition, inserts, reads, updates, and deletes.

  • Itโ€™s flexible enough for small scripts and powerful enough for CLI tools.


๐Ÿš€ Getting Started๏ƒ

Letโ€™s walk through setting things up step-by-step!


1. Bootstrapping the Database๏ƒ

Create a file like db.py to initialize your SQLite database.

# db.py
from luminadb import Database

db = Database(":memory:")  # or use "your_file.db" to persist data

This creates a simple in-memory SQLite database. For persistent storage, pass a filename instead of ":memory:".


2. Creating a Model๏ƒ

Each model is a Python class with typed fields and a schema definition.

# model/notes.py
from uuid import uuid4
from luminadb import model, BaseModel, Primary
from ..db import db

@model(db)
class Notes(BaseModel):
    __schema__ = (Primary('id'),)
    __auto_id__ = lambda: str(uuid4())

    id: str
    title: str
    content: str

๐Ÿ’ก How it works๏ƒ

  • @model(db) binds the class to the database.

  • __schema__ defines your table schema. You can use Primary(), Unique(), Foreign(), etc.

  • __auto_id__ is optional. It helps generate IDs automatically (e.g. UUIDs).


3. Running CRUD Operations๏ƒ

CREATE๏ƒ

Notes.create(title="Hello", content="World!")

You can gather user input too:

title = input('Title: ')
content = input('Content: ')
Notes.create(title=title, content=content)

READ๏ƒ

You can fetch all rows, one row, or use filters:

Notes.all()                  # Returns a list of all notes
Notes.first(id="123")        # Returns the first match or None
Notes.one(id="123")          # Returns exactly one result, else throws error
Advanced .where() filtering๏ƒ
# Find a note by title
note = Notes.where(title="Shopping List").fetch_one()

# Limit or offset
Notes.where().limit(5).fetch()     # Get top 5 notes
Notes.where().offset(5).fetch()    # Skip 5 and get the rest

# Count matching rows
count = Notes.where().count()

UPDATE๏ƒ

Updating a row is super simple. Fetch it first, then call .update():

note = Notes.first(id="some-id")
note.update(title="Updated", content="Updated content here.")

DELETE๏ƒ

Delete just like youโ€™d update:

note = Notes.first(id="some-id")
note.delete()

๐ŸŽฎ Example App โ€“ CRUD in Action๏ƒ

Hereโ€™s a small CLI app to tie it all together:

from enum import IntEnum
from uuid import uuid4
from luminadb import Database, model, BaseModel, Primary, SKIP

db = Database(":memory:")

@model(db)
class Notes(BaseModel):
    __schema__ = (Primary('id'),)
    __auto_id__ = lambda: str(uuid4())

    id: str
    title: str
    content: str

def display():
    print('-'*3)
    for note in Notes.all():
        print(f"ID      : {note.id}")
        print(f"Title   : {note.title}")
        print(f"Content : {note.content}")
        print("-"*3)

def read(prompt: str):
    try:
        return input(prompt)
    except KeyboardInterrupt:
        return SKIP

def create():
    title = input("Title: ")
    content = input("Content: ")
    Notes.create(title=title, content=content)

def update():
    note_id = input('ID: ')
    note = Notes.first(id=note_id)
    if note:
        title = read("New title: ")
        content = read("New content: ")
        note.update(title=title, content=content)
    else:
        print("Note not found.")

def delete():
    note_id = input('ID: ')
    note = Notes.first(id=note_id)
    if note:
        note.delete()
    else:
        print("Note not found.")

class CMD(IntEnum):
    DISPLAY = 1
    CREATE = 2
    UPDATE = 3
    DELETE = 4
    EXIT = 5

def main():
    while True:
        print('-'*8)
        print('1. Display all notes')
        print('2. Create a note')
        print('3. Update a note')
        print('4. Delete a note')
        print('5. Exit')
        try:
            cmd = int(input("Command: "))
            if cmd == CMD.DISPLAY:
                display()
            elif cmd == CMD.CREATE:
                create()
            elif cmd == CMD.UPDATE:
                update()
            elif cmd == CMD.DELETE:
                delete()
            elif cmd == CMD.EXIT:
                break
        except KeyboardInterrupt:
            break
        except Exception as exc:
            print(f"{type(exc).__name__}: {exc}")

if __name__ == "__main__":
    main()

๐Ÿ™‹ FAQ๏ƒ

Q: Can I define relationships between models? A: Yes, using Foreign() in __schema__. This doc will be updated with examples soon.

Q: Does it support migrations? A: Not directly. The schema is defined per-model, so migrations require manual changes.

Q: How are models stored? A: The models reflect SQLite tables. Everything is automatically synced on class definition.


๐Ÿ’ก Tips & Notes๏ƒ

  • You can use any primitive Python types (e.g. int, str, float) in your model class.

  • Keep __auto_id__ short and efficient โ€” UUIDs are recommended.

  • Donโ€™t forget to call Database() only once and reuse the instance.