All notable changes to FastAdmin are documented in this file.
Correctness and security fixes from a full-codebase review. The only public API
addition is the backward-compatible has_action_permission model-admin hook.
- Actions: the
actionendpoint now enforces permissions server-side via a newhas_action_permissionhook (defaults tohas_change_permission), so a read-only admin can no longer run a registered bulk action. Override the hook to allow a read-only user to run a specific non-mutating action. - Passwords: editing a user with an empty password field no longer overwrites the stored hash with a hash of the empty string.
- JWT: signing and verifying are refused when
ADMIN_SECRET_KEYis unset or empty, closing an empty-secret token-forgery path and returning a clear configuration error instead of an HTTP 500.
- All ORMs: a legitimate primary key / user id of
0no longer misroutes a save (update vs insert) or is rejected as unauthenticated. - SQLAlchemy:
get_model_pk_nameresolves non-autoincrement primary keys (UUID/string) instead of falling back to"id";contains/icontainsfilters cast to text so they work on integer columns; the primary key is excluded fromrequired; and the generated primary key is read before commit so create/update is safe under the defaultexpire_on_commit=True. - Tortoise / Django / Yara: a falsy database default (
0/False/"") no longer marks a field as required, and the primary key is excluded fromrequired. Tortoise enum options now emit the scalar value, and Django choice options no longer swap the stored value with the human-readable label. - Serialization: a field listed in both
excludeandlist_displaynow stays excluded from API responses. - Validation: malformed date/datetime values, empty-condition filters
(
field__), unsupported or null export formats, and malformed request bodies on the Django integration now return HTTP 422 instead of an unhandled 500. - Settings:
ADMIN_QUERY_MAX_LIMITandADMIN_SESSION_EXPIRED_ATfall back to their defaults on a blank or non-numeric value instead of crashing the package at import.
Bug-fix follow-up to the 0.8.1 audit. No public API changes.
- SQLAlchemy: relationship (m2m) filter values are coerced to
intfor integer primary keys (previously raised HTTP 500 on strictly typed drivers such as PostgreSQL/asyncpg), and unsupported lookups on relation fields (e.g.icontains) are rejected with HTTP 422 instead of silently matching by primary-key equality. - Pony ORM: the foreign-key filter rewrite no longer corrupts filters on
plain columns whose name merely ends with
_id, and list-view search now runs as a single OR'd WHERE clause instead of materializing every matching row once per search field. - Flask:
HTTPExceptions with an attached response (abort(Response(...))) are passed through unchanged, error headers (Allow,WWW-Authenticate,Retry-After, ...) are preserved on JSON error responses, and unhandled errors are logged with their traceback. - Filtering:
IS NULLis expressible again on text columns viafield__exact=null(substring lookups keep the literal string"null"). - Frontend: stop corrupting text fields whose content is the literal word
"true"/"false"on the change form (booleans are now only coerced for boolean widgets), stop shape-based date detection for fields known to have no date widget, and pass the model configuration directly totransformDataFromServerso every call site gets the safe behavior.
Bug-fix release from a full-source audit of main. No public API changes.
- Pony ORM: fix list-view search crashing (HTTP 500) for models whose
primary key is not named
id, and fix multi-field ordering with mixed ascending/descending fields silently sorting by the wrong priority. - SQLAlchemy: filtering a list by a many-to-many (or other relationship) field no longer raises an unhandled 500; it now matches on the related row's primary key.
- Flask: unhandled server errors now return a proper HTTP 500 with a generic
message (previously sent as HTTP 200 and leaked the exception text), and API
errors are returned as JSON
{"detail": ...}matching the Django/FastAPI integrations instead of HTML error pages. - Filtering: the literal strings
"true","false", and"null"are no longer coerced tobool/Noneon text fields, so a text column can be filtered for those exact values. - Frontend: fix a form-save crash when a JSON field value is an object with a
datekey; stop corrupting text fields whose content looks like a date/time; render storedCheckboxGroupselections correctly; surface errors from dashboard action run/refresh; and use the react-query v5invalidateQueriessignature to avoid over-invalidating unrelated queries. - Correct the
sortable_bydocstring to match actual behavior.
- Add a
register_encoder(type_, encoder)hook to control how a type is serialized in every admin API response (e.g. a custom datetime format, anEnum's label, or a domain value object). Custom encoders take precedence over the built-indatetime/UUID/Decimalhandling (#136).
- Add support for Yara ORM — a fast,
async Python ORM with a Rust engine. New
YaraOrmModelAdmin/YaraOrmInlineModelAdminadmin classes and afastadmin[yara-orm]extra, with a runnable FastAPI example (examples/fastapi_yaraorm). - Add a refresh button to the widget action results (toolbar and expand modal) so results can be re-run in place without losing scroll position (#132).
Security hardening release. No changes to the documented public API, but several defaults are now stricter — review the notes below before upgrading.
- Server-side permission enforcement:
add/change/delete/exportnow enforce the matchinghas_*_permissionhook (previously UI-only), andchange_passwordrequireshas_change_permissionto change another user's password (fixes an account-takeover path). - Mass-assignment protection: form saves only write fields allowed by
fields/exclude/readonly_fields, so a crafted payload can no longer set hidden or read-only columns (e.g.is_superuser). Passwords are hashed viachange_passwordon edit as well as create. - Injection fixes: Pony ORM filters/search are parameterized (were built
from f-strings); SQLAlchemy ordering uses column expressions instead of raw
text(), and LIKE/ILIKE wildcards in filter/search values are escaped. - Filter allowlist & lookups: filters are validated against
list_filter(when set) and restricted to a fixed set of lookups, blocking relation- spanning/regex oracles over hidden columns. - CSV export injection: exported cells starting with
=,+,-or@are neutralized; CSV and JSON exports now emit the same columns. - Cookies & DoS: the session cookie is now
Secure+SameSite=Laxby default (configurable), list/exportlimitis capped byADMIN_QUERY_MAX_LIMIT, and internal error details are no longer returned to clients. - New settings:
ADMIN_SESSION_COOKIE_SECURE,ADMIN_SESSION_COOKIE_SAMESITE,ADMIN_QUERY_MAX_LIMIT.ADMIN_SESSION_EXPIRED_ATis now coerced toint. - Frontend: file/image upload links and
view_on_siterejectjavascript:/data:URIs.
Upgrade note: for local HTTP development set ADMIN_SESSION_COOKIE_SECURE=false,
and set list_filter/fields where you relied on filtering arbitrary columns.
- Restructure the project tooling: PEP 621
pyproject.tomlwith theuv_buildbackend and a committeduv.lock(poetry removed). - Replace black/isort/mypy with
ruff check,ruff formatandty. - Replace the custom HTML documentation with an MkDocs Material site deployed to GitHub Pages: https://vsdudakov.github.io/fastadmin/.
- Add a root
CHANGELOG.md; renameLICENSEtoLICENSE.md; rewriteREADME.md. - Rework GitHub workflows: split CI (lint, backend test matrix, frontend, per-extras install checks) and add docs deploy and tag-driven PyPI release workflows.
- Upgrade all backend and frontend dependencies (latest minors; antd 6.5, eslint 10.6, vitest 4.1).
- No changes to the public FastAdmin API.
- Add
objparameter toupload_filemethod. - Add
get_file_urlmethod to model admin.
- Add toolbar actions to widget action results.
- Add table view to widget action results.
- Add JSON view to widget action results.
- Enhance documentation for the
upload_filemethod. - Add
strtype for pk.
- Add
sub_tabto widget action props. - Dark mode support.
- Add
max_heightto widget action props. - Add highlight search results to widget action results.
- Add copy to clipboard to widget action results.
- Add expand results modal to widget action results.
- Add
menu_sectionto model admins.
- Add
parentargument to widget action props. - Add wraps to decorator to keep the original function name and docstring.
- Add
pre_generate_models_schemamethod to ModelAdmin to pre-generate models schema. - Fix examples.
- Replace helmet to update title and description.
- Improve frontend coverage.
- Add
seriesfield to widget action props. Fix examples. - Add search by actions.
- Replace
DashboardWidgetAdminwithwidget_actiondecorator (no backward compatibility). - Fix mobile view.
- Fix upload file functionality. Fix examples.
- Add new upload file functionality (without backward compatibility). See documentation for details.
- Add example for Flask with SQLAlchemy.
- Removed
ADMIN_DISABLE_CROP_IMAGEsetting. UsedisableCropImageprop in UploadImage widget instead.
- Fix API service errors.
- Fix file download issue for actions.
- Add response types for actions.
- Fix Decimal fields handling.
- Fix inline add/change issue.
- Fix inline filter reset issue.
- Fix filter reset issue.
- Fix date/time handling in transform helpers.
- Fix examples.
- Fix JSON textarea handling.
- Fix datetime/time handling in transform helpers.
- Revert
get_orm_listmethod to original implementation. We can uselist_select_relatedandsearch_fieldsforprefetch_related_fieldsandadditional_search_fields.
- Enhance
get_orm_listmethod to supportprefetch_related_fieldsandadditional_search_fields. - Add request/user context on
BaseModelAdminfor per-request custom logic.
- Fix sort by and search by relations. Fix examples.
- Fix
DetachedInstanceErrorwhen session is closed after commit. - Fix list display widths.
- Fix Flask issues.
- Add
formfield_overridesexample. Addlabelandhelpprops.
- Fix SQLAlchemy required fields. Fix CI.
- Clean up documentation. Update dependencies. Fix linters and tests. Frontend refactoring.
- Fix upload base64 widget; add
disableCropImageprop. Fix examples.
- Fix cleaning of async select fields on forms.
- Fix
_idfields handling. Bump backend and frontend packages.
- Fix
is_pkfor Tortoise ORM.
- Fix M2M/FK handling for SQLAlchemy with PostgreSQL (convert str to int).
- Fix FK handling for SQLAlchemy with PostgreSQL (convert str to int).
- Add
ADMIN_DISABLE_CROP_IMAGEsetting to configure image cropping on upload.
- Fix password logic for user model.
- Make permission functions awaitable. Bump frontend and backend packages.
- Fix edit page frontend issue for Date field.
- Remove python-dotenv dependency. Bump Django. Add Django example.
- Fix examples. Fix Pony ORM (delete, update M2M). Allow sorting by custom columns. Fix
list_displayordering.
- Fix empty M2M issue. Optimize unit tests. Fix Pony ORM. Optimize Tortoise ORM search.
- Fix modal inline dialogs. Fix M2M multiple select.
- Fix SQLAlchemy delete functionality. Add more examples.
- Fix helper functions. Add regex support.
- Add edit button for async select.
- Fix async select in inlines.
- Fix dashboard widgets and auto-register inlines.
- Fix filter issue on list views. Remove Jinja from dependencies.
- Fix datetime-related bugs.
- Update packages. Fix linters and tests in Vite frontend. Remove Pydantic from dependencies.
- Update packages. Use Vite instead of deprecated react-scripts.