Skip to content

Database

Within most projects there is a need for a database. Within starlette_core we use SQLAlchemy.

Declarative Base

Due to the fact we have created other projects that all inherit from this package such as starlette-auth there needs to be a central place that all predefined tables can inherit from a declarative base class (see the docs).

The starlette_core.database.Base class consists of:

class BaseQuery(Query):
    def get_or_404(self, ident):
        """
        performs a query.get or raises `starlette.exceptions.HTTPException`
        if not found.
        """

@as_declarative(metadata=metadata)
class Base:
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

    def __repr__(self):
        return f"<{self.__class__.__name__}, id={self.id}>"

    def __str__(self):
        return self.__repr__()

    # Default auto incrementing primary key field
    # overwrite as needed
    id = sa.Column(sa.Integer, primary_key=True)

    # Convenience property to query the database for instances of this model
    # using the current session. Equivalent to ``db.session.query(Model)``
    query: BaseQuery

    def save(self) -> None:
        """ save the current instance """

    def delete(self) -> None:
        """ delete the current instance """

    def can_be_deleted(self) -> bool:
        """
        Simple helper to check if the instance has entities
        that will prevent this from being deleted via a protected foreign key.
        """

    def refresh_from_db(self) -> None:
        """ Refresh the current instance from the database """

This class should be used in all sqlalchemy tables like so:

import sqlalchemy as sa
from starlette_core.database import Base

class User(Base):
    email = sa.Column(sa.String(100), nullable=False, index=True, unique=True)

Methods

All tables inheriting from Base will have acess to the class' functionallity. This includes .query, .save(), and .delete().

It is important that all tables(base) are imported from all packages when used:

from db import database
from tables import User

user = User.query.first()

user.email = "user@example.com"
user.save()
user.delete()

Your Project Structure

It's best to have a central place to keep a reference to your database. Assuming you have a db.py file in the root of your apps structure and the contents are as follows:

from starlette_core.database import Database, DatabaseURL, metadata
from app.settings import DATABASE_URL

url = DatabaseURL("sqlite:///./db.sqlite3")

# set engine config options
engine_kwargs = {}

# setup the database
database = Database(DATABASE_URL, engine_kwargs=engine_kwargs)

# once the db is initialised you can import any project 
# and external tables into this file.

# the metadata imported above will be the complete metadata 
# used for your db for the likes of alembic migrations.
from my_project import tables
from some_other_project import tables

The Database Class

The starlette_core.database.Database class is what manages the connection to the database.

class Database:
    engine = None

    def create_all(self) -> None:
        """ create all tables """

    def drop_all(self) -> None:
        """ drop all tables """

    def truncate_all(self, force: bool = False) -> None:
        """ truncate all tables """

Which when defined as above you can run commands like database.create_all() or database.engine.execute("SELECT .....") on.

Sessions

While tables that inherit from starlette_core.database.Base will include useful helpers like save() and delete(), these internally just use a Session object (see the docs).

This can be used directly which is particually helpful if you want to save multiple instances at the same time.

from starlette_core.database import Session

session = Session()

instance = User(email="admin@example.com")

try:
    session.add(instance)
    session.commit()
except:
    session.rollback()

Middleware

Using the DatabaseMiddleware

If you are using the database functionality provided by this package it is important to use the middleware to correctly handle sessions. See docs.