Skip to content

tux.database.controllers.base

Base controller module providing common database functionality.

Classes:

Name Description
BaseController

Provides a base interface for database table controllers.

Classes

BaseController(table_name: str)

Bases: Generic[ModelType]

Provides a base interface for database table controllers.

This generic class offers common CRUD (Create, Read, Update, Delete) operations and utility methods for interacting with a specific Prisma model table. It standardizes database interactions and error handling.

Attributes:

Name Type Description
table Any

The Prisma client's model instance for the specific table.

table_name str

The name of the database table this controller manages.

Initializes the BaseController for a specific table.

Parameters:

Name Type Description Default
table_name str

The name of the Prisma model table (e.g., 'case', 'guild'). This name must match an attribute on the Prisma client instance.

required

Methods:

Name Description
find_one

Finds the first record matching specified criteria.

find_unique

Finds a single record by a unique constraint (e.g., ID).

find_many

Finds multiple records matching specified criteria.

count

Counts records matching the specified criteria.

create

Creates a new record in the table.

update

Updates a single existing record matching the criteria.

delete

Deletes a single record matching the criteria.

upsert

Updates a record if it exists, otherwise creates it.

update_many

Updates multiple records matching the criteria.

delete_many

Deletes multiple records matching the criteria.

execute_transaction

Executes a series of database operations within a transaction.

connect_or_create_relation

Builds a Prisma 'connect_or_create' relation structure.

safe_get_attr

Safely retrieves an attribute from an object, returning a default if absent.

Source code in tux/database/controllers/base.py
Python
def __init__(self, table_name: str) -> None:
    """Initializes the BaseController for a specific table.

    Parameters
    ----------
    table_name : str
        The name of the Prisma model table (e.g., 'case', 'guild').
        This name must match an attribute on the Prisma client instance.
    """
    self.table: Any = getattr(db.client, table_name)
    self.table_name = table_name

Functions

_execute_query(operation: Callable[[], Any], error_msg: str) -> Any async

Executes a database query with standardized error logging.

Wraps the Prisma client operation call in a try-except block, logging any exceptions with a contextual error message.

Parameters:

Name Type Description Default
operation Callable[[], Any]

A zero-argument function (e.g., a lambda) that performs the database call.

required
error_msg str

The base error message to log if an exception occurs.

required

Returns:

Type Description
Any

The result of the database operation.

Raises:

Type Description
Exception

Re-raises any exception caught during the database operation.

Source code in tux/database/controllers/base.py
Python
async def _execute_query(
    self,
    operation: Callable[[], Any],
    error_msg: str,
) -> Any:
    """Executes a database query with standardized error logging.

    Wraps the Prisma client operation call in a try-except block,
    logging any exceptions with a contextual error message.

    Parameters
    ----------
    operation : Callable[[], Any]
        A zero-argument function (e.g., a lambda) that performs the database call.
    error_msg : str
        The base error message to log if an exception occurs.

    Returns
    -------
    Any
        The result of the database operation.

    Raises
    ------
    Exception
        Re-raises any exception caught during the database operation.
    """
    # Create a Sentry span to track database query performance
    if sentry_sdk.is_initialized():
        with sentry_sdk.start_span(op="db.query", description=f"Database query: {self.table_name}") as span:
            span.set_tag("db.table", self.table_name)
            try:
                result = await operation()
                span.set_status("ok")
                return result  # noqa: TRY300
            except Exception as e:
                span.set_status("internal_error")
                span.set_data("error", str(e))
                logger.error(f"{error_msg}: {e}")
                raise
    else:
        try:
            return await operation()
        except Exception as e:
            logger.error(f"{error_msg}: {e}")
            raise
_add_include_arg_if_present(args: dict[str, Any], include: dict[str, bool] | None) -> None

Adds the 'include' argument to a dictionary if it is not None.

Source code in tux/database/controllers/base.py
Python
def _add_include_arg_if_present(self, args: dict[str, Any], include: dict[str, bool] | None) -> None:
    """Adds the 'include' argument to a dictionary if it is not None."""
    if include:
        args["include"] = include
_build_find_args(where: dict[str, Any], include: dict[str, bool] | None = None, order: dict[str, str] | None = None, take: int | None = None, skip: int | None = None, cursor: dict[str, Any] | None = None) -> dict[str, Any]

Constructs the keyword arguments dictionary for Prisma find operations.

Source code in tux/database/controllers/base.py
Python
def _build_find_args(
    self,
    where: dict[str, Any],
    include: dict[str, bool] | None = None,
    order: dict[str, str] | None = None,
    take: int | None = None,
    skip: int | None = None,
    cursor: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Constructs the keyword arguments dictionary for Prisma find operations."""
    args: dict[str, Any] = {"where": where}
    self._add_include_arg_if_present(args, include)
    if order:
        args["order"] = order
    if take is not None:
        args["take"] = take
    if skip is not None:
        args["skip"] = skip
    if cursor is not None:
        args["cursor"] = cursor
    return args
_build_simple_args(key_name: str, key_value: dict[str, Any], include: dict[str, bool] | None = None) -> dict[str, Any]

Constructs simple keyword arguments for Prisma (e.g., create, delete).

Source code in tux/database/controllers/base.py
Python
def _build_simple_args(
    self,
    key_name: str,
    key_value: dict[str, Any],
    include: dict[str, bool] | None = None,
) -> dict[str, Any]:
    """Constructs simple keyword arguments for Prisma (e.g., create, delete)."""
    args = {key_name: key_value}
    self._add_include_arg_if_present(args, include)
    return args
_build_create_args(data: dict[str, Any], include: dict[str, bool] | None = None) -> dict[str, Any]

Constructs keyword arguments for Prisma create operations.

Source code in tux/database/controllers/base.py
Python
def _build_create_args(
    self,
    data: dict[str, Any],
    include: dict[str, bool] | None = None,
) -> dict[str, Any]:
    """Constructs keyword arguments for Prisma create operations."""
    return self._build_simple_args("data", data, include)
_build_update_args(where: dict[str, Any], data: dict[str, Any], include: dict[str, bool] | None = None) -> dict[str, Any]

Constructs keyword arguments for Prisma update operations.

Source code in tux/database/controllers/base.py
Python
def _build_update_args(
    self,
    where: dict[str, Any],
    data: dict[str, Any],
    include: dict[str, bool] | None = None,
) -> dict[str, Any]:
    """Constructs keyword arguments for Prisma update operations."""
    args = {"where": where, "data": data}
    self._add_include_arg_if_present(args, include)
    return args
_build_delete_args(where: dict[str, Any], include: dict[str, bool] | None = None) -> dict[str, Any]

Constructs keyword arguments for Prisma delete operations.

Source code in tux/database/controllers/base.py
Python
def _build_delete_args(
    self,
    where: dict[str, Any],
    include: dict[str, bool] | None = None,
) -> dict[str, Any]:
    """Constructs keyword arguments for Prisma delete operations."""
    return self._build_simple_args("where", where, include)
_build_upsert_args(where: dict[str, Any], create: dict[str, Any], update: dict[str, Any], include: dict[str, bool] | None = None) -> dict[str, Any]

Constructs keyword arguments for Prisma upsert operations.

Source code in tux/database/controllers/base.py
Python
def _build_upsert_args(
    self,
    where: dict[str, Any],
    create: dict[str, Any],
    update: dict[str, Any],
    include: dict[str, bool] | None = None,
) -> dict[str, Any]:
    """Constructs keyword arguments for Prisma upsert operations."""
    args = {
        "where": where,
        "data": {
            "create": create,
            "update": update,
        },
    }
    self._add_include_arg_if_present(args, include)
    return args
find_one(where: dict[str, Any], include: dict[str, bool] | None = None, order: dict[str, str] | None = None) -> ModelType | None async

Finds the first record matching specified criteria.

Parameters:

Name Type Description Default
where dict[str, Any]

Query conditions to match.

required
include dict[str, bool]

Specifies relations to include in the result.

None
order dict[str, str]

Specifies the field and direction for ordering.

None

Returns:

Type Description
ModelType | None

The found record or None if no match exists.

Source code in tux/database/controllers/base.py
Python
async def find_one(
    self,
    where: dict[str, Any],
    include: dict[str, bool] | None = None,
    order: dict[str, str] | None = None,
) -> ModelType | None:
    """Finds the first record matching specified criteria.

    Parameters
    ----------
    where : dict[str, Any]
        Query conditions to match.
    include : dict[str, bool], optional
        Specifies relations to include in the result.
    order : dict[str, str], optional
        Specifies the field and direction for ordering.

    Returns
    -------
    ModelType | None
        The found record or None if no match exists.
    """
    find_args = self._build_find_args(where=where, include=include, order=order)
    return await self._execute_query(
        lambda: self.table.find_first(**find_args),
        f"Failed to find record in {self.table_name} with criteria {where}",
    )
find_unique(where: dict[str, Any], include: dict[str, bool] | None = None) -> ModelType | None async

Finds a single record by a unique constraint (e.g., ID).

Parameters:

Name Type Description Default
where dict[str, Any]

Unique query conditions (e.g., {'id': 1}).

required
include dict[str, bool]

Specifies relations to include in the result.

None

Returns:

Type Description
ModelType | None

The found record or None if no match exists.

Source code in tux/database/controllers/base.py
Python
async def find_unique(
    self,
    where: dict[str, Any],
    include: dict[str, bool] | None = None,
) -> ModelType | None:
    """Finds a single record by a unique constraint (e.g., ID).

    Parameters
    ----------
    where : dict[str, Any]
        Unique query conditions (e.g., {'id': 1}).
    include : dict[str, bool], optional
        Specifies relations to include in the result.

    Returns
    -------
    ModelType | None
        The found record or None if no match exists.
    """
    find_args = self._build_find_args(where=where, include=include)  # Order not applicable for find_unique
    return await self._execute_query(
        lambda: self.table.find_unique(**find_args),
        f"Failed to find unique record in {self.table_name} with criteria {where}",
    )
find_many(where: dict[str, Any], include: dict[str, bool] | None = None, order: dict[str, str] | None = None, take: int | None = None, skip: int | None = None, cursor: dict[str, Any] | None = None) -> list[ModelType] async

Finds multiple records matching specified criteria.

Parameters:

Name Type Description Default
where dict[str, Any]

Query conditions to match.

required
include dict[str, bool]

Specifies relations to include in the results.

None
order dict[str, str]

Specifies the field and direction for ordering.

None
take int

Maximum number of records to return.

None
skip int

Number of records to skip (for pagination).

None
cursor dict[str, Any]

Cursor for pagination based on a unique field.

None

Returns:

Type Description
list[ModelType]

A list of found records, potentially empty.

Source code in tux/database/controllers/base.py
Python
async def find_many(
    self,
    where: dict[str, Any],
    include: dict[str, bool] | None = None,
    order: dict[str, str] | None = None,
    take: int | None = None,
    skip: int | None = None,
    cursor: dict[str, Any] | None = None,
) -> list[ModelType]:
    """Finds multiple records matching specified criteria.

    Parameters
    ----------
    where : dict[str, Any]
        Query conditions to match.
    include : dict[str, bool], optional
        Specifies relations to include in the results.
    order : dict[str, str], optional
        Specifies the field and direction for ordering.
    take : int, optional
        Maximum number of records to return.
    skip : int, optional
        Number of records to skip (for pagination).
    cursor : dict[str, Any], optional
        Cursor for pagination based on a unique field.

    Returns
    -------
    list[ModelType]
        A list of found records, potentially empty.
    """
    find_args = self._build_find_args(
        where=where,
        include=include,
        order=order,
        take=take,
        skip=skip,
        cursor=cursor,
    )
    return await self._execute_query(
        lambda: self.table.find_many(**find_args),
        f"Failed to find records in {self.table_name} with criteria {where}",
    )
count(where: dict[str, Any]) -> int async

Counts records matching the specified criteria.

Parameters:

Name Type Description Default
where dict[str, Any]

Query conditions to match.

required

Returns:

Type Description
int

The total number of matching records.

Source code in tux/database/controllers/base.py
Python
async def count(
    self,
    where: dict[str, Any],
) -> int:
    """Counts records matching the specified criteria.

    Parameters
    ----------
    where : dict[str, Any]
        Query conditions to match.

    Returns
    -------
    int
        The total number of matching records.
    """
    return await self._execute_query(
        lambda: self.table.count(where=where),
        f"Failed to count records in {self.table_name} with criteria {where}",
    )
create(data: dict[str, Any], include: dict[str, bool] | None = None) -> ModelType async

Creates a new record in the table.

Parameters:

Name Type Description Default
data dict[str, Any]

The data for the new record.

required
include dict[str, bool]

Specifies relations to include in the returned record.

None

Returns:

Type Description
ModelType

The newly created record.

Source code in tux/database/controllers/base.py
Python
async def create(
    self,
    data: dict[str, Any],
    include: dict[str, bool] | None = None,
) -> ModelType:
    """Creates a new record in the table.

    Parameters
    ----------
    data : dict[str, Any]
        The data for the new record.
    include : dict[str, bool], optional
        Specifies relations to include in the returned record.

    Returns
    -------
    ModelType
        The newly created record.
    """
    create_args = self._build_create_args(data=data, include=include)
    return await self._execute_query(
        lambda: self.table.create(**create_args),
        f"Failed to create record in {self.table_name} with data {data}",
    )
update(where: dict[str, Any], data: dict[str, Any], include: dict[str, bool] | None = None) -> ModelType | None async

Updates a single existing record matching the criteria.

Parameters:

Name Type Description Default
where dict[str, Any]

Query conditions to find the record to update.

required
data dict[str, Any]

The data to update the record with.

required
include dict[str, bool]

Specifies relations to include in the returned record.

None

Returns:

Type Description
ModelType | None

The updated record, or None if no matching record was found.

Source code in tux/database/controllers/base.py
Python
async def update(
    self,
    where: dict[str, Any],
    data: dict[str, Any],
    include: dict[str, bool] | None = None,
) -> ModelType | None:
    """Updates a single existing record matching the criteria.

    Parameters
    ----------
    where : dict[str, Any]
        Query conditions to find the record to update.
    data : dict[str, Any]
        The data to update the record with.
    include : dict[str, bool], optional
        Specifies relations to include in the returned record.

    Returns
    -------
    ModelType | None
        The updated record, or None if no matching record was found.
    """
    update_args = self._build_update_args(where=where, data=data, include=include)
    return await self._execute_query(
        lambda: self.table.update(**update_args),
        f"Failed to update record in {self.table_name} with criteria {where} and data {data}",
    )
delete(where: dict[str, Any], include: dict[str, bool] | None = None) -> ModelType | None async

Deletes a single record matching the criteria.

Parameters:

Name Type Description Default
where dict[str, Any]

Query conditions to find the record to delete.

required
include dict[str, bool]

Specifies relations to include in the returned deleted record.

None

Returns:

Type Description
ModelType | None

The deleted record, or None if no matching record was found.

Source code in tux/database/controllers/base.py
Python
async def delete(
    self,
    where: dict[str, Any],
    include: dict[str, bool] | None = None,
) -> ModelType | None:
    """Deletes a single record matching the criteria.

    Parameters
    ----------
    where : dict[str, Any]
        Query conditions to find the record to delete.
    include : dict[str, bool], optional
        Specifies relations to include in the returned deleted record.

    Returns
    -------
    ModelType | None
        The deleted record, or None if no matching record was found.
    """
    delete_args = self._build_delete_args(where=where, include=include)
    return await self._execute_query(
        lambda: self.table.delete(**delete_args),
        f"Failed to delete record in {self.table_name} with criteria {where}",
    )
upsert(where: dict[str, Any], create: dict[str, Any], update: dict[str, Any], include: dict[str, bool] | None = None) -> ModelType async

Updates a record if it exists, otherwise creates it.

Parameters:

Name Type Description Default
where dict[str, Any]

Query conditions to find the existing record.

required
create dict[str, Any]

Data to use if creating a new record.

required
update dict[str, Any]

Data to use if updating an existing record.

required
include dict[str, bool]

Specifies relations to include in the returned record.

None

Returns:

Type Description
ModelType

The created or updated record.

Source code in tux/database/controllers/base.py
Python
async def upsert(
    self,
    where: dict[str, Any],
    create: dict[str, Any],
    update: dict[str, Any],
    include: dict[str, bool] | None = None,
) -> ModelType:
    """Updates a record if it exists, otherwise creates it.

    Parameters
    ----------
    where : dict[str, Any]
        Query conditions to find the existing record.
    create : dict[str, Any]
        Data to use if creating a new record.
    update : dict[str, Any]
        Data to use if updating an existing record.
    include : dict[str, bool], optional
        Specifies relations to include in the returned record.

    Returns
    -------
    ModelType
        The created or updated record.
    """
    upsert_args = self._build_upsert_args(where=where, create=create, update=update, include=include)
    return await self._execute_query(
        lambda: self.table.upsert(**upsert_args),
        f"Failed to upsert record in {self.table_name} with where={where}, create={create}, update={update}",
    )
update_many(where: dict[str, Any], data: dict[str, Any]) -> int async

Updates multiple records matching the criteria.

Parameters:

Name Type Description Default
where dict[str, Any]

Query conditions to find the records to update.

required
data dict[str, Any]

The data to update the records with.

required

Returns:

Type Description
int

The number of records updated.

Raises:

Type Description
ValueError

If the database operation does not return a valid count.

Source code in tux/database/controllers/base.py
Python
async def update_many(
    self,
    where: dict[str, Any],
    data: dict[str, Any],
) -> int:
    """Updates multiple records matching the criteria.

    Parameters
    ----------
    where : dict[str, Any]
        Query conditions to find the records to update.
    data : dict[str, Any]
        The data to update the records with.

    Returns
    -------
    int
        The number of records updated.

    Raises
    ------
    ValueError
        If the database operation does not return a valid count.
    """
    result = await self._execute_query(
        lambda: self.table.update_many(where=where, data=data),
        f"Failed to update records in {self.table_name} with criteria {where} and data {data}",
    )
    # Validate and return count
    count_val = getattr(result, "count", None)
    if count_val is None or not isinstance(count_val, int):
        msg = f"Update operation for {self.table_name} did not return a valid count, got: {count_val}"
        raise ValueError(msg)
    return count_val
delete_many(where: dict[str, Any]) -> int async

Deletes multiple records matching the criteria.

Parameters:

Name Type Description Default
where dict[str, Any]

Query conditions to find the records to delete.

required

Returns:

Type Description
int

The number of records deleted.

Raises:

Type Description
ValueError

If the database operation does not return a valid count.

Source code in tux/database/controllers/base.py
Python
async def delete_many(
    self,
    where: dict[str, Any],
) -> int:
    """Deletes multiple records matching the criteria.

    Parameters
    ----------
    where : dict[str, Any]
        Query conditions to find the records to delete.

    Returns
    -------
    int
        The number of records deleted.

    Raises
    ------
    ValueError
        If the database operation does not return a valid count.
    """
    result = await self._execute_query(
        lambda: self.table.delete_many(where=where),
        f"Failed to delete records in {self.table_name} with criteria {where}",
    )
    # Validate and return count
    count_val = getattr(result, "count", None)
    if count_val is None or not isinstance(count_val, int):
        msg = f"Delete operation for {self.table_name} did not return a valid count, got: {count_val}"
        raise ValueError(msg)
    return count_val
execute_transaction(callback: Callable[[], Any]) -> Any async

Executes a series of database operations within a transaction.

Ensures atomicity: all operations succeed or all fail and roll back. Note: Does not use _execute_query internally to preserve specific transaction context in error messages.

Parameters:

Name Type Description Default
callback Callable[[], Any]

An async function containing the database operations to execute.

required

Returns:

Type Description
Any

The result returned by the callback function.

Raises:

Type Description
Exception

Re-raises any exception that occurs during the transaction.

Source code in tux/database/controllers/base.py
Python
async def execute_transaction(self, callback: Callable[[], Any]) -> Any:
    """Executes a series of database operations within a transaction.

    Ensures atomicity: all operations succeed or all fail and roll back.
    Note: Does not use _execute_query internally to preserve specific
          transaction context in error messages.

    Parameters
    ----------
    callback : Callable[[], Any]
        An async function containing the database operations to execute.

    Returns
    -------
    Any
        The result returned by the callback function.

    Raises
    ------
    Exception
        Re-raises any exception that occurs during the transaction.
    """
    try:
        async with db.transaction():
            return await callback()
    except Exception as e:
        logger.error(f"Transaction failed in {self.table_name}: {e}")
        raise
connect_or_create_relation(id_field: str, model_id: Any, create_data: dict[str, Any] | None = None) -> dict[str, Any] staticmethod

Builds a Prisma 'connect_or_create' relation structure.

Simplifies linking or creating related records during create/update operations.

Parameters:

Name Type Description Default
id_field str

The name of the ID field used for connection (e.g., 'guild_id').

required
model_id Any

The ID value of the record to connect to.

required
create_data dict[str, Any]

Additional data required if creating the related record. Must include at least the id_field and model_id.

None

Returns:

Type Description
dict[str, Any]

A dictionary formatted for Prisma's connect_or_create.

Source code in tux/database/controllers/base.py
Python
@staticmethod
def connect_or_create_relation(
    id_field: str,
    model_id: Any,
    create_data: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Builds a Prisma 'connect_or_create' relation structure.

    Simplifies linking or creating related records during create/update operations.

    Parameters
    ----------
    id_field : str
        The name of the ID field used for connection (e.g., 'guild_id').
    model_id : Any
        The ID value of the record to connect to.
    create_data : dict[str, Any], optional
        Additional data required if creating the related record.
        Must include at least the `id_field` and `model_id`.

    Returns
    -------
    dict[str, Any]
        A dictionary formatted for Prisma's connect_or_create.
    """
    where = {id_field: model_id}
    # Create data must contain the ID field for the new record
    create = {id_field: model_id}
    if create_data:
        create |= create_data

    return {
        "connect_or_create": {
            "where": where,
            "create": create,
        },
    }
safe_get_attr(obj: Any, attr: str, default: Any = None) -> Any staticmethod

Safely retrieves an attribute from an object, returning a default if absent.

Parameters:

Name Type Description Default
obj Any

The object to retrieve the attribute from.

required
attr str

The name of the attribute.

required
default Any

The value to return if the attribute is not found. Defaults to None.

None

Returns:

Type Description
Any

The attribute's value or the default value.

Source code in tux/database/controllers/base.py
Python
@staticmethod
def safe_get_attr(obj: Any, attr: str, default: Any = None) -> Any:
    """Safely retrieves an attribute from an object, returning a default if absent.

    Parameters
    ----------
    obj : Any
        The object to retrieve the attribute from.
    attr : str
        The name of the attribute.
    default : Any, optional
        The value to return if the attribute is not found. Defaults to None.

    Returns
    -------
    Any
        The attribute's value or the default value.
    """
    return getattr(obj, attr, default)