Skip to content

Reference: The Model Class

All models in your application inherit from db.Model. This base class provides the Active Record-style API for querying and persistence.

The methods documented here are available on all your model classes (e.g., User.where(...)) and their instances (e.g., my_user.save()).

duo_orm.basemodel._DuoOrmMethods

A mixin providing the core Active Record-style API for DuoORM models.

This class is not intended to be used directly by end-users. It is automatically mixed into the db.Model base class that is generated by each Database instance.

where classmethod

where(*args)

Starts a query with a WHERE clause.

This is the primary entry point for building queries.

Parameters:

Name Type Description Default
*args Any

One or more SQLAlchemy filter expressions.

()

Returns:

Type Description
'QueryBuilder[T]'

A new QueryBuilder instance ready for chaining or execution.

all classmethod

all()

Fetches all records for this model.

This is a convenience shortcut for Model.where().all().

Returns:

Type Description
list[T] | Awaitable[list[T]]

A list of model instances (sync) or an awaitable that returns a list (async).

first classmethod

first()

Fetches the first record for this model.

This is a convenience shortcut for Model.where().first().

Returns:

Type Description
Optional[T] | Awaitable[Optional[T]]

A model instance or None (sync), or an awaitable that returns a

Optional[T] | Awaitable[Optional[T]]

model instance or None (async).

count classmethod

count()

Counts all records for this model.

Returns:

Type Description
int | Awaitable[int]

The total row count (sync) or an awaitable resolving to the count (async).

related classmethod

related(relationship_attr, **kwargs)

Starts a query and configures eager loading for a relationship.

This is the primary tool for solving the N+1 query problem.

Parameters:

Name Type Description Default
relationship_attr

The model's relationship attribute (e.g., User.posts).

required
**kwargs Dict[str, Any]

Additional options passed to the QueryBuilder.related method, such as loader, where, or aggregate.

{}

Returns:

Type Description
'QueryBuilder[T]'

A new QueryBuilder instance with eager loading configured.

order_by classmethod

order_by(*args, **kwargs)

Starts a query with an ORDER BY clause.

Parameters:

Name Type Description Default
*args str

Field names to order by. Prefix with - for descending order.

()

Returns:

Type Description
'QueryBuilder[T]'

A new QueryBuilder instance with ordering applied.

paginate classmethod

paginate(*args, **kwargs)

Starts a query with pagination (LIMIT/OFFSET) applied.

Parameters:

Name Type Description Default
limit int

The number of records to return.

required
offset int

The number of records to skip.

required

Returns:

Type Description
'QueryBuilder[T]'

A new QueryBuilder instance with pagination applied.

iterate classmethod

iterate(*args, **kwargs)

Stream records (or batches) using QueryBuilder.iterate.

Parameters:

Name Type Description Default
batch_size

size of each fetch batch (defaults to 200).

required
batch

when True, yields lists of models; when False, yields one model at a time.

required

get classmethod

get(*pk_values, **pk_kwargs)

Fetch a single record by primary key. Supports positional args for single PK or keyword args for composite keys (field names must match column keys). Returns None when not found.

from_schema classmethod

from_schema(payload)

Build an unsaved instance from a Pydantic model or dict.

create classmethod

create(data=None, **kwargs)

Convenience classmethod to build and persist a new instance in one call. Accepts a dict or keyword args. Returns the saved instance.

create_bulk classmethod

create_bulk(
    rows,
    *,
    with_hooks=False,
    batch_size=200,
    return_models=False
)

Bulk insert records. By default skips per-row hooks/validation for speed. Set with_hooks=True to run validate() and timestamp hooks on each row.

update_bulk classmethod

update_bulk(
    values,
    *,
    with_hooks=False,
    batch_size=200,
    require_filter=True,
    per_batch_transaction=True
)

Convenience wrapper for QueryBuilder.update_bulk on the whole table or filtered query.

delete_bulk classmethod

delete_bulk(
    *,
    with_hooks=False,
    batch_size=200,
    require_filter=True,
    per_batch_transaction=True
)

Convenience wrapper for QueryBuilder.delete_bulk.

save

save()

Saves the current instance to the database (INSERT for new, UPDATE for existing).

This method automatically runs .validate() and applies timestamp hooks before persisting the object.

update

update(data=None, **kwargs)

Apply changes to the instance and persist them (hooks/validation run).

delete

delete()

Deletes the current instance from the database.

The operation is cascaded to related objects based on the relationship configuration (e.g., cascade="all, delete-orphan").

apply_schema

apply_schema(payload)

Apply a Pydantic model or dict to this instance without saving.

Partial updates are supported; missing/None fields are ignored.

to_schema

to_schema(schema_cls)

Serialize this instance into the provided Pydantic schema class.

validate

validate()

Hook for subclasses to implement custom validation logic.

This method is called automatically before .save() and .create_bulk() (when with_hooks=True). To block persistence, implementations of this method should raise a ValidationError.

Raises:

Type Description
ValidationError

If the model's state is invalid.

fields classmethod

fields()

Returns a tuple of all mapped column names for this model.

Returns:

Type Description
Tuple[str, ...]

A tuple of strings representing the model's field names.

to_dict

to_dict()

Serializes the instance's column values to a dictionary.

This method only includes values from columns, not from relationships or other Python attributes.

Returns:

Type Description
Dict[str, Any]

A dictionary mapping column names to their values.

Bulk safety guard

update_bulk and delete_bulk default to require_filter=True to prevent accidental full-table writes. Set it to False only when you explicitly intend to touch every row.