File Storage¶
AMSDAL provides a File model and pluggable storage backends for managing file uploads and downloads.
File Model¶
File is a regular AMSDAL Model — it supports all standard operations like save(), asave(), objects.filter(), etc. When a File is saved, AMSDAL automatically calls pre_create()/pre_update() hooks that persist the file content to the configured storage backend before writing the record to the database.
Creating files¶
from amsdal.models.core.file import File
# From bytes
file = File.from_bytes('document.txt', b'Hello, World!')
file.save()
# From a file path
from pathlib import Path
file = File.from_file(Path('/path/to/report.pdf'))
file.save()
# From a file object
with open('image.png', 'rb') as f:
file = File.from_file(f)
file.save()
Reading files¶
# Read all bytes
content = file.read_bytes()
# Open as stream
with file.open() as f:
content = f.read()
# Write to disk
file.to_file(Path('/tmp/output.pdf'))
# Get MIME type
print(file.mimetype) # e.g. 'application/pdf'
Getting URLs¶
url = file.url()
The URL format depends on the storage backend:
- FileSystemStorage:
https://cdn.example.com/media/document.txt - DBStorage:
db://document.txt(not publicly accessible) - S3Storage: presigned S3 URL
Async operations¶
# Save
file = File.from_bytes('doc.txt', b'content')
await file.asave()
# Read
content = await file.aread_bytes()
# Open
async with await file.aopen() as f:
content = await f.read()
# URL
url = await file.aurl()
Storage Backends¶
DBStorage¶
Stores file bytes in the database alongside the File record's data field. This is the default backend.
from amsdal_models.storage.backends.db import DBStorage
class MyModel(Model):
attachment: File = FileField(storage=DBStorage())
- No external storage infrastructure needed
- Files stored in the same database as your models
- Not suitable for large files or high-traffic scenarios
FileSystemStorage¶
Stores files on the local filesystem.
from amsdal.storages.file_system import FileSystemStorage
class MyModel(Model):
attachment: File = FileField(
storage=FileSystemStorage('/var/media', 'https://cdn.example.com/media/'),
)
Parameters:
| Parameter | Description |
|---|---|
base_dir |
Root directory for stored files. Falls back to MEDIA_ROOT setting. |
base_url |
URL prefix for public access. Falls back to MEDIA_URL setting. |
If base_dir and base_url are omitted, the values from settings are used:
# Uses MEDIA_ROOT and MEDIA_URL from settings
storage = FileSystemStorage()
S3Storage (via amsdal_storages)¶
Stores files in Amazon S3. Install the amsdal_storages package:
pip install amsdal_storages
from amsdal_storages.s3.storage import S3Storage
class MyModel(Model):
attachment: File = FileField(
storage=S3Storage(
bucket='my-bucket',
region_name='us-east-1',
object_prefix='uploads/',
presign_ttl=3600,
),
)
Settings¶
Configure default file storage behavior via settings or environment variables:
| Setting | Env Variable | Default | Description |
|---|---|---|---|
MEDIA_ROOT |
AMSDAL_MEDIA_ROOT |
./media |
Root directory for filesystem storage |
MEDIA_URL |
AMSDAL_MEDIA_URL |
/media/ |
URL prefix for file access |
DEFAULT_FILE_STORAGE |
AMSDAL_DEFAULT_FILE_STORAGE |
amsdal_models.storage.backends.db.DBStorage |
Default storage backend class |
Custom Storage Backend¶
Create a custom backend by subclassing Storage:
from amsdal_models.storage.base import Storage
from amsdal_models.storage.types import FileProtocol
from typing import BinaryIO, IO, Any
class MyCloudStorage(Storage):
keeps_local_copy = False
def __init__(self, bucket: str, **kwargs):
self.bucket = bucket
def save(self, file: FileProtocol, content: BinaryIO) -> str:
# Upload content to your cloud provider
key = f'{self.bucket}/{file.filename}'
self._upload(key, content)
return file.filename
def open(self, file: FileProtocol, mode: str = 'rb') -> IO[Any]:
# Return a file-like stream from your cloud provider
return self._download_stream(file.filename)
def delete(self, file: FileProtocol) -> None:
self._remove(file.filename)
def exists(self, file: FileProtocol) -> bool:
return self._check_exists(file.filename)
def url(self, file: FileProtocol) -> str:
return f'https://{self.bucket}.cloud.example.com/{file.filename}'
def _export_kwargs(self) -> dict[str, Any]:
return {'bucket': self.bucket}
For async support, also implement asave(), aopen(), adelete(), aexists(), and aurl().
Setting as default¶
from amsdal.storages import set_default_storage
set_default_storage(MyCloudStorage(bucket='my-app'))
Or configure via environment:
AMSDAL_DEFAULT_FILE_STORAGE=myapp.storages.MyCloudStorage