Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: ci
name: Build Docs
on:
push:
branches:
Expand Down Expand Up @@ -29,4 +29,7 @@ jobs:
pip install mkdocs-material
pip install mkdocs-git-revision-date-localized-plugin
pip install termynal
pip install mdx_include
pip install 'mkdocstrings[python]'
# TODO: Use uv to install docs optional dependencies
- run: mkdocs gh-deploy --force
7 changes: 1 addition & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
.vscode/
.history/
*.vsix
.idea
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
.PHONY: migrations

lint:
uv run pre-commit install
uv run pre-commit run -a -v
uvx pre-commit install
uvx pre-commit run -a -v

update:
uv lock --upgrade
uv run pre-commit autoupdate -j 10
uvx pre-commit autoupdate -j 10

sync:
uv sync --all-extras
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ uv pip install fastapi-async-sql
</div>

## Features
- AsyncSQLAlchemyMiddleware: A middleware to handle database connections with AsyncSQLAlchemy
- AsyncSQLModelMiddleware: A middleware to handle database connections with AsyncSQLAlchemy
- [SQLModel](https://sqlmodel.tiangolo.com/): A library to handle database models with Pydantic and SQLAlchemy
- Base models for `SQLModel`:
- `BaseSQLModel`: A opinionated base model for SQLAlchemy models
Expand Down
11 changes: 11 additions & 0 deletions docs/api_doc/async_sql_model_middleware.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# AsyncSQLModelMiddleware
The `AsyncSQLModelMiddleware` class is a FastAPI middleware that provides an asynchronous SQLModel session (`AsyncSession`) for each request. It ensures that each request has a database session available via `request.state.db`.

::: fastapi_async_sql.middlewares.AsyncSQLModelMiddleware
options:
show_root_heading: true
merge_init_into_class: false
group_by_category: false
members:
- __init__
- dispatch
18 changes: 18 additions & 0 deletions docs/api_doc/base_repository.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# BaseRepository
The `BaseRepository` class is a generic repository that provides default methods for Create, Read, Update, and Delete (CRUD) operations. It works with any SQLModel model and an asynchronous database session.

::: fastapi_async_sql.repositories.BaseRepository
options:
show_root_heading: true
merge_init_into_class: false
group_by_category: false
members:
- __init__
- get
- get_by_ids
- get_count
- get_multi
- get_multi_paginated
- create
- update
- remove
10 changes: 10 additions & 0 deletions docs/api_doc/base_sql_model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# BaseSQLModel
The `BaseSQLModel` class is an opinionated base class for SQLModel models.

::: fastapi_async_sql.models.BaseSQLModel
options:
show_root_heading: true
merge_init_into_class: false
group_by_category: false
members:
- __tablename__
11 changes: 11 additions & 0 deletions docs/api_doc/base_timestamp_model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# BaseTimestampModel
The BaseTimestampModel class is a mixin that adds created_at and updated_at timestamp fields to your models.

::: fastapi_async_sql.models.BaseTimestampModel
options:
show_root_heading: true
merge_init_into_class: false
group_by_category: false
members:
- created_at
- updated_at
10 changes: 10 additions & 0 deletions docs/api_doc/base_uuid_model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# BaseUUIDModel
The `BaseUUIDModel` class is a mixin that adds a UUID-based primary key field to your models. This field is automatically populated with a UUID4 value when a record is created.

::: fastapi_async_sql.models.BaseUUIDModel
options:
show_root_heading: true
merge_init_into_class: false
group_by_category: false
members:
- id
Empty file added docs/concepts/filtering.md
Empty file.
32 changes: 32 additions & 0 deletions docs/concepts/middlewares.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Middlewares

This module provides middleware for integrating SQLModel with FastAPI using asynchronous SQLAlchemy sessions. The main class in this module is `AsyncSQLModelMiddleware`.

## `AsyncSQLModelMiddleware`
??? api "API Documentation"

[fastapi_async_sql.middlewares.AsyncSQLModelMiddleware](../api_doc/async_sql_model_middleware.md)
The `AsyncSQLModelMiddleware` class is a FastAPI middleware that provides an asynchronous SQLModel session (`AsyncSession`) for each request.
It ensures that each request has a database session available via `request.state.db`.

### Usage Example
Here’s how you can integrate `AsyncSQLModelMiddleware` into your FastAPI application:


```python hl_lines="15 25-26 34"
{!./docs_src/middlewares/middleware.py!}
```

### How It Works
1. Initialization: The middleware is initialized with either a db_url or a custom_engine. It creates an async engine and an async session maker bound to the engine.
2. Request Handling: During request handling, the middleware creates an async session and attaches it to the request.state.db. This session is used throughout the request and is automatically disposed of after the request is processed.
3. Dependency Injection: You can access the session in your route handlers by extracting it from request.state.db using FastAPI's dependency injection system.

### Example Configuration
To use `AsyncSQLModelMiddleware`, you can either pass a database URL or a custom SQLAlchemy async engine. Here’s an example configuration:

```python hl_lines="11 15"
{!./docs_src/middlewares/main.py!}
```

With this setup, every request will have access to an async SQLAlchemy session, making it easy to interact with your database using SQLModel in an async manner.
120 changes: 120 additions & 0 deletions docs/concepts/models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Models

The `models` module in the `fastapi-async-sql` package provides base classes that can be used to create SQLAlchemy models with asynchronous support in FastAPI applications.

These base classes include functionality for automatic table naming, timestamp fields, and UUID-based primary keys.

## Base Classes

### `BaseSQLModel`
??? api "API Documentation"

[fastapi_async_sql.models.BaseSQLModel](../api_doc/base_sql_model.md)

The `BaseSQLModel` class is a base model that extends SQLModel and adds asynchronous capabilities via SQLAlchemy's `AsyncAttrs`.

It automatically generates the table name based on the class name and provides a configuration that supports camelCase aliasing, assignment validation, and strict field handling.

#### Example Usage

```python
{!./docs_src/models/model.py!}
```

#### Features
- Automatic Table Naming: The table name is automatically generated from the class name and converted to snake_case plural form.
- Pydantic Configuration: Configured to use camelCase for JSON serialization, validate field assignments, populate fields by name, and forbid extra fields.

#### How It Works
- Table Naming: The `__tablename__` attribute is generated using the `to_snake_plural` function, which converts the class name from PascalCase to snake_case and pluralizes it. For example, a class named Item would have a table name items.
- Pydantic Config: The `model_config` attribute defines how the model behaves with Pydantic, ensuring proper aliasing and validation.

### `BaseTimestampModel`
??? api "API Documentation"

[fastapi_async_sql.models.BaseTimestampModel](../api_doc/base_timestamp_model.md)

The `BaseTimestampModel` class is a mixin that adds `created_at` and `updated_at` timestamp fields to your models. These fields are automatically populated with the current UTC time when a record is created or updated.
#### Example Usage
```python
{!./docs_src/models/timestamp_model.py!}
```
#### Features
- Automatically populates the `created_at` field with the current UTC time when a record is created.
- Automatically updates the `updated_at` field with the current UTC time when a record is updated.

#### How It Works
- `created_at`: Uses `default_factory` to set the current UTC time when the record is created.
- `updated_at`: Uses onupdate to set the current UTC time whenever the record is updated. If the record is not updated, the field remains None.

### `BaseUUIDModel`
??? api "API Documentation"

[fastapi_async_sql.models.BaseUUIDModel](../api_doc/base_uuid_model.md)

The `BaseUUIDModel` class is a mixin that adds a UUID-based primary key field to your models. This field is automatically populated with a UUID4 value when a record is created.

#### Example Usage

```python
{!./docs_src/models/uuid_model.py!}
```

#### Features
- Automatically generates a UUID4 value for the `id` field when a record is created.
- Ensures that each record has a unique UUID as the primary key.

#### How It Works
- `UUID Generation`: The `id` field is set to use the `uuid4` function as the default value, ensuring a unique UUID is generated for each record.


## Combining Base Classes
You can combine the base classes to create models with multiple features. For example, you can create a model with both timestamp fields and a UUID-based primary key.

### Example Usage
```python
{!./docs_src/models/combined_model.py!}
```

### Features
This model would have the following features:

- Automatic table naming based on the class name.
- CamelCase aliasing and field validation.
- `created_at` and `updated_at` timestamp fields.
- `id` primary key field with UUID4 values.
- Automatic population of timestamp and UUID fields on record creation.
- Proper JSON serialization and validation behavior.
- Strict field handling and aliasing.

## Using Models with `AsyncAttrs` to Prevent Implicit I/O

### Understanding Implicit I/O

In asynchronous applications, it's crucial to avoid implicit I/O operations, especially when working with SQLAlchemy's ORM and lazy-loading relationships. Implicit I/O can occur when you access relationship attributes or deferred columns that haven't been loaded yet. Under traditional asyncio, accessing these attributes directly can lead to errors because the I/O operation needed to fetch the data is not allowed to occur implicitly.

### What is `AsyncAttrs`?

`AsyncAttrs` is a mixin provided by SQLAlchemy that helps manage these situations by enabling attributes to be accessed in an awaitable manner. When you use `AsyncAttrs`, any attribute that might trigger a lazy load or deferred column access can be accessed using the `awaitable_attrs` attribute. This ensures that any required I/O operation is explicitly awaited, thus avoiding implicit I/O errors.

### How to Use `AsyncAttrs` in `fastapi-async-sql`

The `BaseSQLModel` class in `fastapi-async-sql` includes the `AsyncAttrs` mixin, which makes it easy to work with asynchronous I/O when using SQLModel.

Here’s how you can use it:

1. **Define your Models**: Your models should inherit from `BaseSQLModel`, which already includes the `AsyncAttrs` mixin.

2. **Access Relationships**: When accessing a relationship attribute that is lazy-loaded, use the `awaitable_attrs` accessor to explicitly await the attribute.

#### Example Usage

```python hl_lines="31"
{!./docs_src/models/async_attrs_model.py!}
```

## Utility Functions
The models module relies on utility functions to handle string conversions and pluralization. These functions are used to generate table names and convert between different naming conventions.

- `to_camel`: Converts a string from snake_case to camelCase.
- `to_snake_plural`: Converts a string from PascalCase to snake_case plural form.
Empty file added docs/concepts/pagination.md
Empty file.
118 changes: 118 additions & 0 deletions docs/concepts/repositories.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Repositories

The `repositories` module in the `fastapi-async-sql` package provides a generic repository pattern implementation to simplify CRUD operations for SQLModel models in FastAPI applications. This module abstracts common database operations, allowing developers to focus on business logic rather than repetitive CRUD operations.

## BaseRepository
??? api "API Documentation"

[fastapi_async_sql.repositories.BaseRepository](../api_doc/base_repository.md)

The `BaseRepository` class is a generic repository that provides default methods for Create, Read, Update, and Delete (CRUD) operations. It works with any SQLModel model and an asynchronous database session.


### Example Usage

Here’s an example of how to use the `BaseRepository` in a FastAPI application:
#### Create SQLModel models
The first step is to define your SQLModel models. These models represent the tables in your database and define the structure of your data.
```python
# Code above omitted 👆

{!./docs_src/repositories/repository.py[ln:7]!}


{!./docs_src/repositories/repository.py[ln:15-19]!}

# Code below omitted 👇
```
/// details | 👀 Full file preview
```python

{!./docs_src/repositories/repository.py!}
```
///

#### Create schemas (data models)
Next, create Pydantic schemas to validate and serialize your data. These schemas define the structure of the data that will be sent to and received from your FastAPI endpoints.
```python
# Code above omitted 👆

{!./docs_src/repositories/repository.py[ln:22-30]!}

# Code below omitted 👇
```
/// details | 👀 Full file preview
```python

{!./docs_src/repositories/repository.py!}
```
///

#### Create a repository
Finally, create a repository for your model by extending the `BaseRepository` class. This repository will handle all the CRUD operations for your model.
```python
# Code above omitted 👆

{!./docs_src/repositories/repository.py[ln:33-35]!}

# Code below omitted 👇
```
/// details | 👀 Full file preview
```python

{!./docs_src/repositories/repository.py!}
```
///

#### Create a dependency
To use the repository in your FastAPI application, you can create a dependency that initializes the repository with the database session.
```python
# Code above omitted 👆

{!./docs_src/repositories/repository.py[ln:37-41]!}

# Code below omitted 👇
```

/// details | 👀 Full file preview
```python

{!./docs_src/repositories/repository.py!}
```
///

#### Use the repository
You can now use the repository in your FastAPI application to interact with your database. The repository provides methods for creating, reading, updating, and deleting objects in the database.
```python
# Code above omitted 👆

{!./docs_src/repositories/repository.py[ln:44-]!}

```
/// details | 👀 Full file preview
```python

{!./docs_src/repositories/repository.py!}
```
///

## Exception Handling

The `repositories` module makes use of custom exceptions to handle specific error scenarios:

- `CreateObjectError`: Raised when there is an error creating an object in the database.
- `ObjectNotFoundError`: Raised when an object is not found in the database by its primary key.

These exceptions help provide more meaningful error messages and make it easier to debug issues in your application.

## Filtering and Pagination

The `get_multi` and `get_multi_paginated` methods support filtering and pagination through the `Filter` and `Params` classes, respectively. These features make it easier to manage large datasets and retrieve only the data you need.

## Additional resources
- [SQLModel documentation](https://sqlmodel.tiangolo.com/tutorial/fastapi/multiple-models/)
- [Pydantic documentation](https://docs.pydantic.dev/latest/)
- [FastAPI documentation](https://fastapi.tiangolo.com/)
- [SQLAlchemy documentation](https://docs.sqlalchemy.org/)
- [FastAPI Pagination documentation](https://uriyyo-fastapi-pagination.netlify.app/)
- [FastAPI Filter documentation](https://fastapi-filter.netlify.app/)
Loading