### Complete Example with CASCADE Deletion Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/foreign-keys/explicit.md A full example demonstrating the setup of Author and Book models with CASCADE on_delete and on_update actions, including insertion, querying, and deletion. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel, ForeignKey class Author(BaseDBModel): name: str email: str class Book(BaseDBModel): title: str author_id: int = ForeignKey( Author, on_delete="CASCADE", on_update="CASCADE" ) # Create database and tables db = SqliterDB(":memory:") db.create_table(Author) db.create_table(Book) # Insert an author author = db.insert(Author( name="Jane Austen", email="jane@example.com" )) # Insert books by this author book1 = db.insert(Book( title="Pride and Prejudice", author_id=author.pk )) book2 = db.insert(Book( title="Sense and Sensibility", author_id=author.pk )) # Query books by author jane_books = db.select(Book).filter(author_id=author.pk).fetch_all() print(f"Jane has {len(jane_books)} books") # Delete the author (CASCADE will delete the books) db.delete(Author, author.pk) # Verify books are deleted remaining_books = db.select(Book).fetch_all() assert len(remaining_books) == 0 ``` -------------------------------- ### Complete ORM Foreign Key Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/foreign-keys/orm.md A full example demonstrating ORM foreign key setup, including model definitions, database initialization, and data insertion. ```python from sqliter import SqliterDB from sqliter.orm import BaseDBModel, ForeignKey class Author(BaseDBModel): name: str email: str class Book(BaseDBModel): title: str year: int author: ForeignKey[Author] = ForeignKey(Author, on_delete="CASCADE") # Create database and tables db = SqliterDB(":memory:") db.create_table(Author) db.create_table(Book) # Insert an author author = db.insert(Author( name="Jane Austen", email="jane@example.com" )) ``` -------------------------------- ### Install SQLiter-Py Demo Source: https://github.com/seapagan/sqliter-py/blob/main/README.md Run this command to install the necessary packages for the SQLiter-Py interactive demo. ```bash uv add sqliter-py[demo] ``` -------------------------------- ### Install SQLiter-py with Poetry Source: https://github.com/seapagan/sqliter-py/blob/main/README.md Install the SQLiter-py library using Poetry. ```bash poetry add sqliter-py ``` -------------------------------- ### Install SQLiter-py with Async Support Source: https://github.com/seapagan/sqliter-py/blob/main/docs/quickstart.md Shows how to install the 'async' extra for SQLiter-py to enable asyncio support. ```bash uv add 'sqliter-py[async]' # or pip install 'sqliter-py[async]' ``` -------------------------------- ### Install SQLiter-py with uv Source: https://github.com/seapagan/sqliter-py/blob/main/README.md Install the SQLiter-py library using the uv package manager. ```bash uv add sqliter-py ``` -------------------------------- ### Install SQLiter-py with pip Source: https://github.com/seapagan/sqliter-py/blob/main/README.md Install the SQLiter-py library using pip. ```bash pip install sqliter-py ``` -------------------------------- ### Install SQLiter-py with Demo Extra Source: https://github.com/seapagan/sqliter-py/blob/main/docs/index.md Install the library with the 'demo' extra to enable the interactive terminal-based demo. This command can be used with either 'uv' or 'pip'. ```bash uv add sqliter-py[demo] ``` ```bash pip install sqliter-py[demo] ``` -------------------------------- ### Install Async Extra Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/asyncio.md Install the async extra for SQLiter using pip. ```bash uv add 'sqliter-py[async]' ``` -------------------------------- ### Install SQLiter-py with extras extra using uv Source: https://github.com/seapagan/sqliter-py/blob/main/docs/installation.md Install the 'extras' extra with uv to include Inflect for improved pluralization. ```bash uv add 'sqliter-py[extras]' ``` -------------------------------- ### Install Project Dependencies Source: https://github.com/seapagan/sqliter-py/blob/main/CONTRIBUTING.md Install all project dependencies, including optional extras required for testing. This command uses uv to manage the virtual environment and packages. ```console uv sync --all-extras ``` -------------------------------- ### Install SQLiter-py with full extra using uv Source: https://github.com/seapagan/sqliter-py/blob/main/docs/installation.md Install the 'full' extra with uv to include all optional dependencies (aiosqlite, Textual, Inflect). ```bash uv add sqliter-py[full] ``` -------------------------------- ### Example: Get Table Name Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/base-model.md Demonstrates how to call the get_table_name class method on a model to determine its corresponding database table name. ```python class UserProfile(BaseDBModel): name: str UserProfile.get_table_name() # "user_profiles" ``` -------------------------------- ### Run SQLiter-Py Demo Source: https://github.com/seapagan/sqliter-py/blob/main/README.md Execute this command after installation to launch the interactive TUI demo for exploring SQLiter features. ```bash sqliter-demo ``` -------------------------------- ### Async Context Manager Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-sqliterdb.md Example of using AsyncSqliterDB with an async with statement. Remember to call db.close() explicitly after exiting the block. ```python async with AsyncSqliterDB("app.db") as db: ... ``` -------------------------------- ### Async Connection Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/async.md Shows how to create an AsyncSqliterDB instance, connect to the database, and check connection status. The connection is established explicitly with `await db.connect()`. ```python import asyncio from sqliter.asyncio import AsyncSqliterDB async def main(): db = AsyncSqliterDB(memory=True) print(f"Created database: {db}") print(f"Is memory: {db.is_memory}") print(f"Filename: {db.filename}") await db.connect() print(f"Connected: {db.is_connected}") await db.close() print(f"After close: {db.is_connected}") asyncio.run(main()) ``` -------------------------------- ### Async Query Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-sqliterdb.md Demonstrates fetching multiple records asynchronously with filtering and ordering. ```python users = await ( db.select(User) .filter(active=True) .order("name") .limit(20) .fetch_all() ) ``` -------------------------------- ### Basic Setup and Model Definition Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/guide.md Import necessary modules and define a Pydantic model for your database table. Then, create a database connection using SqliterDB. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel class User(BaseDBModel): name: str age: int email: str # Create a database connection db = SqliterDB("example.db") ``` -------------------------------- ### Install Python with uv Source: https://github.com/seapagan/sqliter-py/blob/main/CONTRIBUTING.md Use uv to install a specific Python version if it's not already available locally. This ensures the correct Python environment for the project. ```console uv python install 3.14 ``` -------------------------------- ### Run the SQLiter TUI Demo Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/index.md Start the interactive TUI demo for SQLiter by running it as a Python module or using the command-line shortcut. ```bash python -m sqliter.tui ``` ```bash sqliter-demo ``` -------------------------------- ### AsyncReverseManyToMany Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-orm.md Demonstrates accessing reverse many-to-many relationships. This example fetches all articles associated with a given tag. ```python articles = await tag.articles.fetch_all() ``` -------------------------------- ### Async ORM Fetch Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-sqliterdb.md Shows how to fetch an ORM object asynchronously and then fetch its related foreign key object. ```python book = await db.get(Book, 1) if book is None: raise ValueError("Book not found") author = await book.author.fetch() ``` -------------------------------- ### Pagination Logic Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/ordering.md Illustrates a common pattern for calculating the offset value for pagination based on the current page number and page size. ```python page = 2 page_size = 10 offset_value = (page - 1) * page_size results = db.select(User).limit(page_size).offset(offset_value).fetch_all() ``` -------------------------------- ### Complete SqliterDB Properties Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/properties.md Demonstrates accessing filename, is_memory, is_autocommit, and table_names properties on a SqliterDB instance. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel # Define a simple model class User(BaseDBModel): id: int name: str # Create an in-memory database db = SqliterDB(memory=True) db.create_table(User) # Access properties print(db.filename) # Output: None print(db.is_memory) # Output: True print(db.is_autocommit) # Output: True (this is the default) print(db.table_names) # Output: ['user'] ``` -------------------------------- ### AsyncManyToMany Manager Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-orm.md Shows how to add an instance to a many-to-many relationship and fetch all related instances. Uses the `AsyncManyToManyManager`. ```python await article.tags.add(tag) tags = await article.tags.fetch_all() ``` -------------------------------- ### Install Git Pre-Commit Hooks Source: https://github.com/seapagan/sqliter-py/blob/main/CONTRIBUTING.md Install git pre-commit hooks to ensure code quality checks are performed locally before commits are made. This helps maintain a consistent codebase. ```console $ prek install prek installed at .git/hooks/pre-commit ``` -------------------------------- ### Get Cache Controls Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/caching.md Demonstrates how to get cache statistics, bypass the cache for specific calls, and override the Time-To-Live (TTL) for individual lookups. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel class Product(BaseDBModel): name: str price: float db = SqliterDB(memory=True, cache_enabled=True, cache_ttl=60) db.create_table(Product) product = db.insert(Product(name="Widget", price=19.99)) db.get(Product, product.pk) stats = db.get_cache_stats() print("After first get (miss):", stats) db.get(Product, product.pk) stats = db.get_cache_stats() print("After second get (hit):", stats) db.get(Product, product.pk, bypass_cache=True) stats = db.get_cache_stats() print("After bypass_cache=True:", stats) db.get(Product, product.pk, cache_ttl=5) print("Per-call TTL override set to 5s for this lookup") db.close() ``` -------------------------------- ### Async Query with Grouping and Aggregation Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-query-builder.md Example demonstrating how to group results by a field, annotate with a count, and fetch the results as dictionaries using the async query builder. ```python report = await ( db.select(User) .group_by("status") .annotate(total=func.count()) .fetch_dicts() ) ``` -------------------------------- ### Selective Caching Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/caching.md Illustrates selective caching by creating two database instances: one with caching enabled for read-heavy workloads and another without caching for write-heavy workloads. ```python # For read-heavy workloads db_cached = SqliterDB(db_filename="mydb.db", cache_enabled=True, cache_ttl=300) reports = db_cached.select(Sales).fetch_all() # For write-heavy workloads db_fresh = SqliterDB(db_filename="mydb.db", cache_enabled=False) for record in new_records: db_fresh.insert(record) ``` -------------------------------- ### Example Usage of ReverseQuery Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/orm.md Demonstrates common operations on reverse relationships, including fetching all related items, filtering and counting, and checking for existence. ```python # Fetch all books by an author books = author.books.fetch_all() # Filter and count count = author.books.filter(title__contains="Python").count() # Check existence has_books = author.books.exists() ``` -------------------------------- ### Lazy FK Access Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-orm.md Demonstrates lazy loading of a foreign key relationship. Accessing `book.author` returns a loader, which must be awaited to fetch the related object. ```python book = await db.get(Book, 1) loader = book.author author = await loader.fetch() ``` -------------------------------- ### QueryBuilder Filter Examples Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/query-builder.md Demonstrates various ways to use the `filter` method, including simple equality, comparison operators, chaining multiple filters for AND logic, and filtering based on related model fields. ```python # Simple equality (default __eq) db.select(User).filter(name="Alice") # Comparison operators db.select(User).filter(age__gt=18, age__lt=65) # Multiple chained calls (AND logic) db.select(User).filter(active=True).filter(age__gte=21) # Relationship traversal (ORM mode) db.select(Book).filter(author__name="Alice") ``` -------------------------------- ### Relationship Path Examples for select_related Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/orm.md Illustrates different ways to specify relationship paths for nested loading using `select_related`, including single-level, two-level, multiple paths, and deep nesting. ```python # Single level select_related("author") # Loads immediate parent # Two levels select_related("book__author") # Loads grandparent # Multiple paths (comma-separated) select_related("author", "publisher") # Loads multiple relationships # Deep nesting (3+ levels) select_related("comment__book__author__country") ``` -------------------------------- ### Optimized String Search Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/string-filters.md Demonstrates optimizing string searches by preferring `endswith` over `contains` when the pattern is known, as `endswith` can better utilize indexes on large datasets. ```python # Instead of this (slower on large datasets): # results = db.select(User).filter(email__contains="@example.com").fetch_all() # Use this when you know the format: # results = db.select(User).filter(email__endswith="@example.com").fetch_all() ``` -------------------------------- ### Foreign Key CASCADE Action Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/foreign-keys/explicit.md Demonstrates the CASCADE action for foreign keys. When the referenced author is deleted, the associated book record is also automatically deleted. ```python class Book(BaseDBModel): title: str author_id: int = ForeignKey( Author, on_delete="CASCADE", on_update="CASCADE" ) author = db.insert(Author(name="Jane Austen", email="jane@example.com")) book = db.insert(Book(title="Pride and Prejudice", author_id=author.pk)) # Deleting the author will also delete the book db.delete(Author, author.pk) # The book is now deleted too books = db.select(Book).filter(author_id=author.pk).fetch_all() assert len(books) == 0 ``` -------------------------------- ### AsyncReverseManyToMany Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-orm.md The reverse-side descriptor for asynchronous many-to-many relationships, installed when `related_name` is defined. It allows fetching related instances, for example, `tag.articles`. ```APIDOC ## AsyncReverseManyToMany Reverse-side async many-to-many descriptor installed on the related model when `related_name` is defined. Example: ```python articles = await tag.articles.fetch_all() ``` ``` -------------------------------- ### Async FK Eager Loading Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/async.md Demonstrates how to use `select_related()` to eagerly load foreign keys in a single query, making related object attributes directly accessible without an additional fetch call. This is the async equivalent of sync lazy loading ergonomics. ```python import asyncio from sqliter.asyncio import AsyncSqliterDB from sqliter.asyncio.orm import AsyncBaseDBModel, AsyncForeignKey class Author(AsyncBaseDBModel): name: str class Book(AsyncBaseDBModel): title: str author: AsyncForeignKey[Author] = AsyncForeignKey(Author) async def main(): db = AsyncSqliterDB(memory=True) await db.create_table(Author) await db.create_table(Book) austen = await db.insert(Author(name="Jane Austen")) await db.insert(Book(title="Pride and Prejudice", author=austen)) await db.insert(Book(title="Emma", author=austen)) # select_related performs a JOIN — author is loaded immediately books = await db.select(Book).select_related("author").fetch_all() for b in books: # No await needed — already loaded via JOIN print(f" {b.title} by {b.author.name}") print(f"\nLoaded {len(books)} books with eager FK") await db.close() asyncio.run(main()) ``` -------------------------------- ### Paginate Records with Limit and Offset Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/guide.md Implement pagination for query results using the limit() and offset() methods to control the number of records returned and their starting point. ```python paginated_users = db.select(User).limit(10).offset(20).fetch_all() ``` -------------------------------- ### Use SQLiter as a Context Manager Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/connection.md This example shows how to use SQLiter as a context manager for automatic transaction handling. Transactions auto-commit on success and auto-rollback on error. Remember to explicitly close the connection after the `with` block. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel class Task(BaseDBModel): title: str done: bool = False print("Using context manager for transactions:\n") db = SqliterDB(memory=True) with db: db.create_table(Task) task = db.insert(Task(title="Learn SQLiter", done=False)) print(f"Inserted: {task.title} (pk={task.pk})") print("Transaction auto-commits on exit") print(f"\nAfter context: connected={db.is_connected}") db.close() ``` -------------------------------- ### Basic Async Query Execution Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-query-builder.md Execute a basic asynchronous query to fetch all users matching a filter. This is a common starting point for async data retrieval. ```python results = await db.select(User).filter(active=True).fetch_all() ``` -------------------------------- ### Transaction Rollback Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/transactions.md Demonstrates automatic rollback of transaction changes when an error occurs within the 'with db:' block. The database state is restored to its condition before the transaction began. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel class Item(BaseDBModel): name: str quantity: int db = SqliterDB(memory=True) db.create_table(Item) item: Item = db.insert(Item(name="Widget", quantity=10)) print(f"Initial quantity: {item.quantity}") # Use context manager for automatic rollback on error try: with db: item.quantity = 5 db.update(item) print("Inside transaction: updated to 5") # If error occurs, changes are rolled back error_msg = "Intentional error for rollback" raise RuntimeError(error_msg) # noqa: TRY301 except RuntimeError: print("Error occurred - transaction rolled back") print("Original value preserved (quantity=10)") db.close() ``` -------------------------------- ### Chaining Filters with AND Logic Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/filters.md Demonstrates how to chain multiple filter conditions using the `filter()` method. All conditions are combined with AND logic, meaning only records matching all specified criteria are returned. This example selects users from NYC who are 30 years or older. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel class User(BaseDBModel): name: str age: int city: str db = SqliterDB(memory=True) db.create_table(User) db.insert(User(name="Alice", age=30, city="NYC")) db.insert(User(name="Bob", age=25, city="LA")) db.insert(User(name="Charlie", age=30, city="NYC")) results = ( db.select(User).filter(age__gte=30).filter(city__eq="NYC").fetch_all() ) print(f"Users in NYC aged 30+: {len(results)}") for user in results: print(f" - {user.name}, {user.age}") db.close() ``` -------------------------------- ### Bypass Cache for a Single get() Call Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/caching.md Allows bypassing the cache for a single `get()` operation by passing `bypass_cache=True`. ```python user = db.get(User, 1, bypass_cache=True) ``` -------------------------------- ### Alternative One-Line Filter Syntax Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/filters.md Shows an alternative syntax for applying multiple filters on a single line. This is useful for concise queries where readability is maintained. This example selects products that are less than $20 and have a stock of at least 50. ```python affordable = db.select(Product).filter( price__lt=20.0, stock__gte=50 ).fetch_all() ``` -------------------------------- ### Pydantic ConfigDict for BaseDBModel Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/base-model.md Configure BaseDBModel's behavior using Pydantic's ConfigDict. This example sets extra fields to be ignored, allows population by name, enables validation on assignment, and permits model creation from objects with attributes. ```python model_config = ConfigDict( extra="ignore", populate_by_name=True, validate_assignment=True, from_attributes=True, ) ``` -------------------------------- ### Override TTL for a Single get() Call Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/caching.md Allows overriding the cache TTL for a single `get()` operation by passing the `cache_ttl` argument. ```python user = db.get(User, 1, cache_ttl=300) ``` -------------------------------- ### AsyncReverseQuery Filter Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-orm.md Filters related objects using an `AsyncReverseQuery`. This example fetches all books by a specific author with a given title. ```python author = await db.get(Author, 1) books = await author.books.filter(title="Guide").fetch_all() ``` -------------------------------- ### Serve MkDocs Locally Source: https://github.com/seapagan/sqliter-py/blob/main/CONTRIBUTING.md Use this command to serve the MkDocs documentation locally for testing and development. ```bash poe docs:serve ``` -------------------------------- ### Serve MkDocs on All Interfaces Source: https://github.com/seapagan/sqliter-py/blob/main/CONTRIBUTING.md This command serves the MkDocs documentation locally and makes it accessible on all network interfaces, allowing viewing from other devices. ```bash poe docs:serve:all ``` -------------------------------- ### Run the Interactive SQLiter-py Demo Source: https://github.com/seapagan/sqliter-py/blob/main/docs/index.md Execute the interactive terminal-based demo for SQLiter-py. This allows hands-on exploration of all library features without writing code. ```bash python -m sqliter.tui ``` -------------------------------- ### Build MkDocs Site Source: https://github.com/seapagan/sqliter-py/blob/main/CONTRIBUTING.md Builds the MkDocs site, outputting the static files into the 'dist' folder. ```bash poe docs:build ``` -------------------------------- ### Get Table Names Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/sqliterdb.md Retrieve a list of all user-defined table names in the database. ```python db = SqliterDB("myapp.db") print(db.table_names) # ["users", "posts", ...] ``` -------------------------------- ### Get Database Filename Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/properties.md Retrieve the filename of the database. Returns None for in-memory databases. ```python db = SqliterDB(db_filename="test.db") print(db.filename) ``` -------------------------------- ### Basic SQLiter-py Usage Source: https://github.com/seapagan/sqliter-py/blob/main/README.md Demonstrates creating a database table, inserting a record, querying, and updating a record using SQLiter-py. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel # Define your model class User(BaseDBModel): name: str age: int # Create a database connection db = SqliterDB("example.db") # Create the table db.create_table(User) # Insert a record user = User(name="John Doe", age=30) new_user = db.insert(user) # Query records results = db.select(User).filter(name="John Doe").fetch_all() for user in results: print(f"User: {user.name}, Age: {user.age}") # Update a record new_user.age = 31 db.update(new_user) ``` -------------------------------- ### get() Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/sqliterdb.md Retrieve a single record by its primary key. Supports cache bypassing and TTL overrides. ```APIDOC ## get() ### Description Retrieve a single record by its primary key. Supports cache bypassing and TTL overrides. ### Method `get` ### Parameters #### Path Parameters - `model_class` (type[T]) - Required - The model class - `primary_key_value` (int) - Required - The primary key value #### Query Parameters - `bypass_cache` (bool) - Optional - Skip cache read/write for this lookup (Default: False) - `cache_ttl` (int | None) - Optional - Optional TTL override for this specific lookup (Default: None) ### Returns `T | None` -- The model instance if found, `None` otherwise. ### Raises - `RecordFetchError` -- If there is an error fetching the record. - `ValueError` -- If `cache_ttl` is negative. ### Example ```python user = db.get(User, 1) if user: print(user.name) ``` ``` -------------------------------- ### Async Database Connection and Operations Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/asyncio.md Demonstrates creating an async database connection, defining a model, and performing basic CRUD operations using AsyncSqliterDB. ```python from sqliter.asyncio import AsyncSqliterDB from sqliter.model import BaseDBModel class User(BaseDBModel): name: str async def main() -> None: db = AsyncSqliterDB("example.db") await db.create_table(User) user = await db.insert(User(name="Ada")) fetched = await db.get(User, user.pk) users = await db.select(User).filter(name="Ada").fetch_all() await db.delete(User, user.pk) await db.close() ``` -------------------------------- ### Import BaseDBModel Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/base-model.md Import the BaseDBModel class from the sqliter.model module to start defining your database models. ```python from sqliter.model import BaseDBModel ``` -------------------------------- ### Get Cached Value Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-sqliterdb.md Retrieves a value from the query cache using a table name and cache key. ```python def cache_get( self, table_name: str, cache_key: str, ) -> tuple[bool, Any]: ``` -------------------------------- ### Connect to Database Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-sqliterdb.md Establishes an asynchronous connection to the database. ```python async def connect(self) -> aiosqlite.Connection: ``` -------------------------------- ### Create a new database file or connect to an existing one Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/connecting.md Instantiate SqliterDB with a database file name to connect or create the database. ```python from sqliter import SqliterDB db = SqliterDB("your_database.db") ``` -------------------------------- ### Get Table Names Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/properties.md Retrieve a list of all user-defined table names in the database. The property will reconnect if the connection is closed. ```python db = SqliterDB(memory=True) db.create_table(User) # Assume 'User' is a predefined model print(db.table_names) ``` -------------------------------- ### Get Table Names Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-sqliterdb.md Asynchronously retrieves a list of all table names in the database. This is a method due to async limitations. ```python async def get_table_names(self) -> list[str]: ``` -------------------------------- ### Get Primary Key Field Name Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/base-model.md Retrieves the name of the primary key field for the model, which is always 'pk'. ```python @classmethod def get_primary_key(cls) -> str: ``` -------------------------------- ### Create an in-memory database using ':memory:' Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/connecting.md An alternative and descriptive way to create an in-memory database by specifying ":memory:" as the database name. ```python db = SqliterDB(":memory:") ``` -------------------------------- ### Enable and Demonstrate Caching Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/caching.md Enables caching and demonstrates its performance benefits by comparing cache miss and cache hit query times. Use this to see the speedup caching provides for repeated queries with the same parameters. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel import tempfile import time from pathlib import Path class User(BaseDBModel): name: str email: str age: int # Use file-based database to show real caching benefits with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f: db_path = f.name db = None try: db = SqliterDB(db_path, cache_enabled=True) db.create_table(User) # Insert more data for a more realistic demo for i in range(50): db.insert( User( name=f"User {i}", email=f"user{i}@example.com", age=20 + i, ) ) print("Inserted 50 users") print("Caching stores query results to avoid repeated I/O\n") # Query with filter (more expensive than simple pk lookup) # First query - cache miss start = time.perf_counter() users = db.select(User).filter(age__gte=40).fetch_all() miss_time = (time.perf_counter() - start) * 1000 print(f"First query (cache miss): {miss_time:.3f}ms") print(f"Found {len(users)} users age 40+") # Second query with same filter - cache hit start = time.perf_counter() users = db.select(User).filter(age__gte=40).fetch_all() hit_time = (time.perf_counter() - start) * 1000 print(f"Second query (cache hit): {hit_time:.3f}ms") print(f"Found {len(users)} users age 40+") # Show speedup if hit_time > 0: speedup = miss_time / hit_time print(f"\nCache hit is {speedup:.1f}x faster!") print("(Benefits increase with query complexity and data size)") finally: if db is not None: db.close() # Cleanup Path(db_path).unlink(missing_ok=True) ``` -------------------------------- ### Create Table with Options Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/sqliterdb.md Creates a database table based on a model class. Use `exists_ok=True` to prevent errors if the table already exists, or `force=True` to drop and recreate the table. ```python db.create_table(User) db.create_table(User, force=True) # Drop and recreate ``` -------------------------------- ### Get Cache Statistics Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-sqliterdb.md Retrieves the current cache statistics, including hit and miss counts, for the asynchronous database connection. ```python def get_cache_stats(self) -> dict[str, int | float]: ``` -------------------------------- ### Constructor Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-sqliterdb.md Initializes a new instance of the AsyncSqliterDB class. Note that `reset=True` is not supported directly and should be used with the `create` factory method. ```APIDOC ## Constructor ```python def __init__( self, db_filename: str | None = None, *, memory: bool = False, auto_commit: bool = True, debug: bool = False, logger: logging.Logger | None = None, reset: bool = False, return_local_time: bool = True, cache_enabled: bool = False, cache_max_size: int = 1000, cache_ttl: int | None = None, cache_max_memory_mb: int | None = None, ) -> None: ``` This matches the sync constructor closely, except `reset=True` is not supported directly in `__init__()`. > [!NOTE] > Use `await AsyncSqliterDB.create(..., reset=True)` instead of passing > `reset=True` to the constructor. ``` -------------------------------- ### Aggregate-Only Projection Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/results.md Use `annotate()` without `group_by()` to get overall summary values for the entire dataset. Results are returned as dictionaries. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel from sqliter.query import func class Sale(BaseDBModel): category: str amount: float db = SqliterDB(memory=True) db.create_table(Sale) db.insert(Sale(category="books", amount=10.0)) db.insert(Sale(category="books", amount=15.0)) db.insert(Sale(category="games", amount=40.0)) summary = ( db.select(Sale) .annotate(total_rows=func.count(), total_amount=func.sum("amount")) .fetch_dicts() ) print(summary) db.close() ``` -------------------------------- ### Get Foreign Keys for a Model Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/orm.md Retrieves a list of foreign key relationships defined for a specific table. Each relationship is returned as a dictionary. ```python from typing import Any class cls: @classmethod def get_foreign_keys( cls, table_name: str, ) -> list[dict[str, Any]]: pass ``` -------------------------------- ### Connect to a database and ignore memory setting Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/connecting.md When `memory=True` is set, specifying a database name is ignored, and the database will be created in memory. ```python db = SqliterDB("ignored.db", memory=True) ``` -------------------------------- ### Get Record by Primary Key Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-sqliterdb.md Asynchronously retrieves a record by its primary key. Supports bypassing cache and custom cache TTL. ```python async def get( self, model_class: type[T], primary_key_value: int, *, bypass_cache: bool = False, cache_ttl: int | None = None, ) -> T | None: ``` -------------------------------- ### Prefetch Many-to-Many Relationships Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/orm.md Eagerly load many-to-many relationships using `prefetch_related()`. This example demonstrates prefetching tags associated with articles. ```python from typing import Any, cast from sqliter import SqliterDB from sqliter.orm import BaseDBModel, ManyToMany class Tag(BaseDBModel): name: str class Article(BaseDBModel): title: str tags: ManyToMany[Tag] = ManyToMany(Tag, related_name="articles") db = SqliterDB(memory=True) db.create_table(Tag) db.create_table(Article) python = db.insert(Tag(name="python")) sqlite = db.insert(Tag(name="sqlite")) orm_tag = db.insert(Tag(name="orm")) a1 = db.insert(Article(title="SQLiter Guide")) a2 = db.insert(Article(title="Python Tips")) a1.tags.add(python, sqlite, orm_tag) a2.tags.add(python) ``` -------------------------------- ### Basic SQLiter-py Usage Source: https://github.com/seapagan/sqliter-py/blob/main/docs/quickstart.md Demonstrates defining a model, connecting to a database, creating a table, inserting, querying, updating, and deleting records using SQLiter-py. ```python from typing import Optional from sqliter import SqliterDB from sqliter.model import BaseDBModel # Define your model class User(BaseDBModel): name: str age: int admin: Optional[bool] = False # Create a database connection db = SqliterDB("example.db") # Create the table db.create_table(User) # Insert a record user = User(name="John Doe", age=30) new_user = db.insert(user) # Query records results = db.select(User).filter(name="John Doe").fetch_all() for user in results: print(f"User: {user.name}, Age: {user.age}, Admin: {user.admin}") # Update a record new_user.age = 31 db.update(new_user) results = db.select(User).filter(name="John Doe").fetch_one() print("Updated age:", results.age) # Delete a record by primary key db.delete(User, new_user.pk) # Delete all records returned from a query: delete_count = db.select(User).filter(age__gt=30).delete() ``` -------------------------------- ### Counting Related Rows (Many-to-Many) Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/aggregates.md Count related records in a many-to-many relationship using `with_count`. This example counts tags for each article. ```python # Many-to-many count (forward or reverse) rows = ( db.select(Article) .with_count("tags", alias="tag_count") .fetch_dicts() ) ``` -------------------------------- ### Initialize SqliterDB Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/sqliterdb.md Instantiate SqliterDB for file-based or in-memory databases, with options for auto-commit, debugging, caching, and resetting. ```python # File-based database db = SqliterDB("myapp.db") # In-memory database db = SqliterDB(memory=True) # With caching enabled db = SqliterDB( "myapp.db", cache_enabled=True, cache_ttl=300, cache_max_memory_mb=50, ) ``` -------------------------------- ### Build Complex Queries with Chaining Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/orm.md Demonstrates building complex queries step-by-step using method chaining. This includes eager loading, filtering by related and direct fields, and ordering results. ```python # Build complex queries step by step query = ( db.select(Book) .select_related("author") # Eager load .filter(author__name="Jane Austen") # Filter by related field .filter(year__gte=1800) # Additional filter .order("year") # Sort results ) results = query.fetch_all() ``` -------------------------------- ### Enable Cache at Database Creation Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/caching.md Demonstrates how to enable caching globally by setting `cache_enabled=True` and configuring a default `cache_ttl` when creating the `SqliterDB` instance. ```python # Enable cache at database creation db = SqliterDB(db_filename="mydb.db", cache_enabled=True, cache_ttl=60) ``` -------------------------------- ### AsyncSqliterDB Constructor Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/async-sqliterdb.md Initializes an AsyncSqliterDB instance. Note that reset=True is not supported directly; use AsyncSqliterDB.create() instead. ```python def __init__( self, db_filename: str | None = None, *, memory: bool = False, auto_commit: bool = True, debug: bool = False, logger: logging.Logger | None = None, reset: bool = False, return_local_time: bool = True, cache_enabled: bool = False, cache_max_size: int = 1000, cache_ttl: int | None = None, cache_max_memory_mb: int | None = None, ) -> None: ``` -------------------------------- ### Order Records by Age (Descending) Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/guide.md Order the results of a query by a specific field in descending order. Useful for sorting data, for example, by age. ```python ordered_users = db.select(User).order("age", reverse=True).fetch_all() ``` -------------------------------- ### Connect to Database Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/sqliterdb.md Establish a connection to the SQLite database and enable foreign key enforcement. ```python def connect(self) -> sqlite3.Connection: # ... implementation details ... ``` -------------------------------- ### Get Record by Primary Key Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/crud.md Retrieves a single record using its primary key. Returns `None` if no record is found with the specified primary key. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel class Task(BaseDBModel): title: str done: bool = False db = SqliterDB(memory=True) db.create_table(Task) task: Task = db.insert(Task(title="Buy groceries")) print(f"Created: {task.title} (pk={task.pk})") retrieved = db.get(Task, task.pk) if retrieved is not None: task_retrieved = retrieved print(f"Retrieved: {task_retrieved.title}") print(f"Same object: {task_retrieved.pk == task.pk}") db.close() ``` -------------------------------- ### Async Query Building and Execution Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/asyncio.md Illustrates building and executing an asynchronous query using AsyncQueryBuilder with filtering, ordering, and limiting. ```python results = await ( db.select(User) .filter(name="Ada") .order("name") .limit(10) .fetch_all() ) ``` -------------------------------- ### Get Model Class by Table Name Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/orm.md Retrieves a model class associated with a given table name. Returns None if no model is found for the specified table. ```python from typing import Any class cls: @classmethod def get_model( cls, table_name: str, ) -> type[Any] | None: pass ``` -------------------------------- ### Transaction Isolation Example Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/transactions.md Illustrates that operations within a transaction are isolated from other operations until the transaction is committed. Changes made inside the transaction are not visible outside until completion. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel class Counter(BaseDBModel): value: int db = SqliterDB(memory=True) db.create_table(Counter) counter = db.insert(Counter(value=0)) with db: # Increment counter counter.value += 1 db.update(counter) # Value is 1 inside transaction print(f"Inside: {counter.value}") # Value is still 1 after commit reloaded = db.get(Counter, counter.pk) if reloaded is not None: print(f"After commit: {reloaded.value}") db.close() ``` -------------------------------- ### Async Transaction Management Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/async.md Demonstrates atomic transaction management using the `async with db:` context manager. Commits on success, rolls back on exceptions. ```python import asyncio import tempfile from pathlib import Path from sqliter.asyncio import AsyncSqliterDB from sqliter.model import BaseDBModel class Account(BaseDBModel): name: str balance: float async def main(): with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f: db_path = f.name db = AsyncSqliterDB(db_filename=db_path) await db.create_table(Account) alice = await db.insert(Account(name="Alice", balance=100.0)) print(f"Initial: Alice=${alice.balance}") try: async with db: alice.balance -= 50.0 await db.update(alice) print("Inside txn: deducted $50") raise RuntimeError("Simulated payment failure") except RuntimeError: print("Error — transaction rolled back") # Verify with a fresh connection db2 = AsyncSqliterDB(db_filename=db_path) restored = await db2.get(Account, alice.pk) if restored is not None: print(f"Restored: Alice=${restored.balance}") if restored.balance == 100.0: print("Rollback confirmed") await db2.close() await db.close() Path(db_path).unlink(missing_ok=True) asyncio.run(main()) ``` -------------------------------- ### Handle Null Foreign Keys Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/foreign-keys/orm.md When a foreign key is null, accessing it directly returns `None`. This example demonstrates inserting a book without an author and retrieving it. ```python class Book(BaseDBModel): title: str author: ForeignKey[Optional[Author]] = ForeignKey( Author, on_delete="SET NULL" ) # Insert book without author book = db.insert(Book(title="Anonymous", author=None)) book = db.get(Book, book.pk) # Returns None for null FK print(book.author) # None ``` -------------------------------- ### Bulk Update Records with SQLiter Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/crud.md Updates multiple records efficiently without writing raw SQL. This example demonstrates updating the status of multiple tasks. ```python from sqliter import SqliterDB from sqliter.model import BaseDBModel class Task(BaseDBModel): title: str status: str = "pending" db = SqliterDB(memory=True) db.create_table(Task) # Insert tasks with different statuses tasks = [ Task(title="Write docs", status="pending"), Task(title="Write tests", status="pending"), Task(title="Review PR", status="in_progress"), Task(title="Deploy app", status="pending"), ] db.bulk_insert(tasks) print("Initial tasks:") for task in db.select(Task).fetch_all(): print(f" - {task.title}: {task.status}") # Example of how to perform a bulk update (code not fully shown in source, but implied by context) db.close() ``` -------------------------------- ### Basic Query Construction and Execution Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/query-builder.md Constructs a query to select users older than 18, ordered by name, and fetches all matching records. This demonstrates chaining filter and order operations. ```python results = db.select(User).filter(age__gt=18).order("name").fetch_all() ``` -------------------------------- ### Async Database Context Manager Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/asyncio.md Shows how to use AsyncSqliterDB as an asynchronous context manager for automatic connection management. ```python async with AsyncSqliterDB("example.db") as db: await db.create_table(User) ``` -------------------------------- ### Fetching Authors and Their Books Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/foreign-keys.md Demonstrates fetching all authors and then iterating to fetch books for each author. This can lead to N+1 query issues if not managed carefully. ```python authors = db.select(Author).fetch_all() for author in authors: books = author.books.fetch_all() # One query per author for book in books: print(f"{book.title} by {author.name}") ``` -------------------------------- ### ReverseManyToMany Descriptor Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/many-to-many.md The ReverseManyToMany descriptor is automatically installed on the target model for a many-to-many relationship, unless suppressed. It provides access to the related instances from the perspective of the target model. ```APIDOC ## ReverseManyToMany Reverse accessor descriptor automatically installed on the target model unless suppressed (symmetrical self-ref). ```python articles = tag.articles.fetch_all() ``` ### Properties: - `sql_metadata -> M2MSQLMetadata` ``` -------------------------------- ### Get Table Name for Model Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/base-model.md Retrieve the database table name associated with a model. This method considers the Meta.table_name attribute or generates a name based on the class name. ```python @classmethod def get_table_name(cls) -> str: ``` -------------------------------- ### Ordering with Indexed Fields Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/ordering.md Demonstrates the performance benefit of ordering by indexed fields. Ordering by an indexed field (e.g., `username`) is significantly faster than ordering by a non-indexed field (e.g., `age`). ```python from sqliter.model.unique import unique class User(BaseDBModel): username: str = unique() # Indexed age: int # Not indexed # Fast: Uses index db.select(User).order("username").fetch_all() # Slower: Requires full table scan db.select(User).order("age").fetch_all() ``` -------------------------------- ### Extract ForeignKeyInfo from FieldInfo Source: https://github.com/seapagan/sqliter-py/blob/main/docs/api-reference/foreign-keys.md Use this function to get foreign key metadata from a Pydantic FieldInfo object. It returns ForeignKeyInfo if the field is a foreign key, otherwise None. ```python from sqliter.model.foreign_key import get_foreign_key_info field_info = Book.model_fields["author_id"] fk_info = get_foreign_key_info(field_info) if fk_info: print(fk_info.to_model) # print(fk_info.on_delete) # "CASCADE" ``` -------------------------------- ### Unique Grouped Rows Without Aggregates Source: https://github.com/seapagan/sqliter-py/blob/main/docs/guide/aggregates.md Retrieve unique values for a specific field by grouping without applying any aggregate functions. Useful for getting distinct categories. ```python categories = db.select(Sale).group_by("category").fetch_dicts() # -> [{"category": "books"}, {"category": "games"}, {"category": "music"}] ``` -------------------------------- ### Prefetching Tags for Articles (Forward M2M) Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/orm.md Demonstrates prefetching related 'tags' for 'Article' objects. This loads all tags for all articles in a single query, caching them on each article instance. ```python from typing import Any, cast from sqliter import SqliterDB from sqliter.orm import BaseDBModel, ManyToMany class Tag(BaseDBModel): name: str class Article(BaseDBModel): title: str tags: ManyToMany[Tag] = ManyToMany(Tag, related_name="articles") db = SqliterDB(memory=True) db.create_table(Tag) db.create_table(Article) python = db.insert(Tag(name="python")) sqlite = db.insert(Tag(name="sqlite")) orm_tag = db.insert(Tag(name="orm")) a1 = db.insert(Article(title="SQLiter Guide")) a2 = db.insert(Article(title="Python Tips")) a1.tags.add(python, sqlite, orm_tag) a2.tags.add(python, orm_tag) articles = db.select(Article).prefetch_related("tags").fetch_all() print("Articles with prefetched tags:") for article in articles: tags = article.tags.fetch_all() tag_names = ", ".join(t.name for t in tags) print(f" {article.title}: [{tag_names}]") # Reverse: prefetch articles for tags tags = db.select(Tag).prefetch_related("articles").fetch_all() print("\nTags with prefetched articles:") for tag in tags: entries = cast("Any", tag).articles.fetch_all() entry_titles = ", ".join(e.title for e in entries) count = cast("Any", tag).articles.count() print(f" {tag.name}: {count} article(s) [{entry_titles}]") db.close() ``` -------------------------------- ### Query Builder Chaining Order Equivalence Source: https://github.com/seapagan/sqliter-py/blob/main/docs/tui-demo/orm.md Illustrates that the order of chaining filter and select_related methods does not affect the generated SQL or performance. Both examples produce identical SQL. ```python # Both examples produce identical SQL - QueryBuilder composes # the query regardless of method chaining order books = ( db.select(Book) .filter(author__name="Jane Austen") .select_related("author") .fetch_all() ) # Equivalent to the above - same SQL, same performance books = ( db.select(Book) .select_related("author") .filter(author__name="Jane Austen") .fetch_all() ) ```