Skip to content

Commit a855102

Browse files
authored
docs: add alembic integration section (#15)
* fix: install project along with docs * chore: move docs to optional deps * docs: add complete alembic section
1 parent f8a26dc commit a855102

File tree

4 files changed

+81
-42
lines changed

4 files changed

+81
-42
lines changed

.readthedocs.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ build:
77

88
sphinx:
99
configuration: docs/src/conf.py
10+
1011
python:
1112
install:
12-
- requirements: docs/requirements.txt
13+
- method: pip
14+
path: .
15+
extra_requirements:
16+
- docs

docs/src/usage.rst

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
Usage
22
=====
3-
43
This section provides practical examples and guidance on using ``fastapi-async-storages``
54
to handle asynchronous file storage in your FastAPI applications.
65

76
Working with storages
87
---------------------
9-
108
Often in projects, you want to get input file in the API and store it somewhere.
119
The `fastapi-async-storages` simplifies the process to store and retrieve the files in a re-usable manner.
1210

@@ -56,7 +54,6 @@ Now let's see a minimal example of using :class:`~async_storages.S3Storage` in a
5654

5755
Working with ORM extensions
5856
---------------------------
59-
6057
The example you saw was useful, but **fastapi-async-storages** has ORM integrations
6158
which makes storing and serving the files easier.
6259

@@ -67,7 +64,6 @@ Support ORM include:
6764

6865
SQLAlchemy
6966
~~~~~~~~~~
70-
7167
You can use custom :code:`SQLAlchemy` types from :code:`fastapi-async-storages` for this.
7268

7369
Supported types include:
@@ -101,32 +97,71 @@ Let's see an example:
10197
)
10298
10399
class Document(Base):
104-
__tablename__ = "documents"
100+
__tablename__ = "documents"
105101
106-
id = Column(Integer, primary_key=True)
107-
file = Column(FileType(storage=storage))
108-
image = Column(ImageType(storage=storage))
102+
id = Column(Integer, primary_key=True)
103+
file = Column(FileType(storage=storage))
104+
image = Column(ImageType(storage=storage))
109105
110106
async def main():
111107
async with engine.begin() as conn:
112108
await conn.run_sync(Base.metadata.create_all)
113109
114-
# create an in-memory image
115-
img_buf = BytesIO()
116-
Image.new("RGB", (32, 16), color=(255, 0, 0)).save(img_buf, format="PNG")
117-
img_buf.seek(0)
118-
119-
# upload and link file and image
120-
img_name await storage.upload(img_buf, "uploads/test-image.png")
121-
file_name = await storage.upload(BytesIO(b"hello world"), "uploads/test.txt")
122-
123-
async with async_session() as session:
124-
doc = Document(file=file_name, image=img_name)
125-
session.add(doc)
126-
await session.commit()
127-
128-
doc = await session.get(Document, doc.id)
129-
url = await doc.file.get_path()
130-
print(url)
131-
width, height = await doc.image.get_dimensions()
132-
print(f"Dimensions: {width}x{height}")
110+
# create an in-memory image
111+
img_buf = BytesIO()
112+
Image.new("RGB", (32, 16), color=(255, 0, 0)).save(img_buf, format="PNG")
113+
img_buf.seek(0)
114+
115+
# upload and link file and image
116+
img_name await storage.upload(img_buf, "uploads/test-image.png")
117+
file_name = await storage.upload(BytesIO(b"hello world"), "uploads/test.txt")
118+
119+
async with async_session() as session:
120+
doc = Document(file=file_name, image=img_name)
121+
session.add(doc)
122+
await session.commit()
123+
124+
doc = await session.get(Document, doc.id)
125+
url = await doc.file.get_path()
126+
print(url)
127+
width, height = await doc.image.get_dimensions()
128+
print(f"Dimensions: {width}x{height}")
129+
130+
Integration with `Alembic <https://alembic.sqlalchemy.org/en/latest/>`_
131+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
132+
By default, custom types are not registered in Alembic's migrations.
133+
To integrate these new types with Alembic, you can do this:
134+
135+
We create the following snippet in ``custom_types.py``:
136+
137+
.. code-block:: python
138+
139+
from typing import Any
140+
from async_storages.integrations.sqlalchemy import FileType as _FileType
141+
142+
from app.core.storages import storage
143+
144+
class FileType(_FileType):
145+
def __init__(self, *args: Any, **kwargs: Any):
146+
super().__init__(storage=storage, *args, **kwargs)
147+
148+
And by using the new :class:`~async_storages.integrations.sqlalchemy.FileType` Alembic can do the imports properly.
149+
150+
Add files path to ``script.py.mako``.
151+
Alembic allows you to modify ``script.py.mako`` and the migrations are generated with proper imports.
152+
153+
.. code-block:: mako
154+
155+
"""${message}
156+
157+
Revision ID: ${up_revision}
158+
Revises: ${down_revision | comma,n}
159+
Create Date: ${create_date}
160+
161+
"""
162+
from alembic import op
163+
import sqlalchemy as sa
164+
import path_to_custom_types_py_file
165+
${imports if imports else ""}
166+
167+
# THE REST OF SCRIPT

pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ dependencies = []
1111

1212
[project.optional-dependencies]
1313
s3 = ["aioboto3>=15.4.0"]
14+
docs = [
15+
"furo>=2025.9.25",
16+
"sphinx>=8.2.3",
17+
]
1418

1519
[dependency-groups]
1620
dev = [
@@ -21,10 +25,6 @@ dev = [
2125
"pytest-asyncio>=1.2.0",
2226
"sqlalchemy>=2.0.44",
2327
]
24-
docs = [
25-
"furo>=2025.9.25",
26-
"sphinx>=8.2.3",
27-
]
2828

2929
[tool.uv.build-backend]
3030
module-root = "src"

uv.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)