diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1c94bba..d4582fb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,14 +1,20 @@
name: FastAdmin CI
-on: [create, push]
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ workflow_dispatch:
permissions:
contents: read
id-token: write
+ pull-requests: write
-concurrency:
+concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
ci:
@@ -20,14 +26,14 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
- python-version: '3.13'
+ python-version: "3.13"
cache: "poetry"
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: v24.3.0
- cache: 'yarn'
- cache-dependency-path: 'frontend/yarn.lock'
+ cache: "yarn"
+ cache-dependency-path: "frontend/yarn.lock"
- name: Install Dependencies
run: make install
- name: Run Lint
diff --git a/docs/build.py b/docs/build.py
index 875aa76..050008c 100644
--- a/docs/build.py
+++ b/docs/build.py
@@ -44,6 +44,13 @@ def read_cls_docstring(cls):
def get_versions():
return [
+ {
+ "version": "0.4.9",
+ "changes": [
+ "Add obj parameter to upload_file method.",
+ "Add get_file_url method to model admin.",
+ ],
+ },
{
"version": "0.4.8",
"changes": [
@@ -426,7 +433,6 @@ def read_example(rel_path: str) -> str:
def get_page_context(page_url):
-
from fastadmin import InlineModelAdmin, ModelAdmin, WidgetType, widget_action
from fastadmin.models.base import BaseModelAdmin
from fastadmin.models.schemas import (
@@ -759,7 +765,27 @@ async def save_model(self, id, payload):
},
{
"type": "text",
- "content": "For file and image fields use UploadFile and UploadImage widgets in formfield_overrides. Implement upload_file(obj, field_name, file_name, file_content) on the model admin to handle uploads; it must return the file URL (e.g. after saving to disk or S3).",
+ "content": "For file and image fields use UploadFile and UploadImage widgets in formfield_overrides. Implement upload_file(field_name, file_name, file_content, obj=None) on the model admin to handle uploads; it must return the file URL (e.g. after saving to disk or S3).",
+ },
+ {
+ "type": "text",
+ "content": "To customise the URL shown in the upload widget (e.g. generate an S3 presigned URL instead of displaying the raw s3:// key), override get_file_url on the model admin. The display URL is emitted as {field_name}__url in the serialised object and passed to the widget as valueRepr; the raw stored value is never changed, so form saves are unaffected.",
+ },
+ {
+ "type": "code-python",
+ "content": """async def get_file_url(self, field_name: str, value: str, obj=None) -> str:
+ # value is the raw stored key, e.g. "s3://bucket/key"
+ # Return a presigned URL so the file can be viewed in the admin.
+ # Example using aiobotocore:
+ #
+ # bucket, key = value.replace("s3://", "").split("/", 1)
+ # async with aiobotocore.session.get_session().create_client("s3") as s3:
+ # return await s3.generate_presigned_url(
+ # "get_object",
+ # Params={"Bucket": bucket, "Key": key},
+ # ExpiresIn=3600,
+ # )
+ return value""",
},
{
"type": "text",
diff --git a/docs/index.html b/docs/index.html
index 865e616..1027047 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -192,6 +192,10 @@
- For file and image fields use UploadFile and UploadImage widgets in formfield_overrides. Implement upload_file(obj, field_name, file_name, file_content) on the model admin to handle uploads; it must return the file URL (e.g. after saving to disk or S3).
+ For file and image fields use UploadFile and UploadImage widgets in formfield_overrides. Implement upload_file(field_name, file_name, file_content, obj=None) on the model admin to handle uploads; it must return the file URL (e.g. after saving to disk or S3).
+ To customise the URL shown in the upload widget (e.g. generate an S3 presigned URL instead of displaying the raw s3:// key), override get_file_url on the model admin. The display URL is emitted as {field_name}__url in the serialised object and passed to the widget as valueRepr; the raw stored value is never changed, so form saves are unaffected.
+
+
+async def get_file_url(self, field_name: str, value: str, obj=None) -> str:
+ # value is the raw stored key, e.g. "s3://bucket/key"
+ # Return a presigned URL so the file can be viewed in the admin.
+ # Example using aiobotocore:
+ #
+ # bucket, key = value.replace("s3://", "").split("/", 1)
+ # async with aiobotocore.session.get_session().create_client("s3") as s3:
+ # return await s3.generate_presigned_url(
+ # "get_object",
+ # Params={"Bucket": bucket, "Key": key},
+ # ExpiresIn=3600,
+ # )
+ return value
+
+
+
+
+
+
+
Use formfield_overrides to customize widget props per field. You can set label for a custom field label and help for description text below the field:
+ Add obj parameter to upload_file method. +
+ + + + + + + + + + + + + + + ++ Add get_file_url method to model admin. +
+ + + + + + + + + + + + + + +