{"id":18187318,"url":"https://github.com/vsdudakov/fastadmin","last_synced_at":"2026-03-05T20:53:56.077Z","repository":{"id":93116020,"uuid":"604944903","full_name":"vsdudakov/fastadmin","owner":"vsdudakov","description":"FastAdmin is an easy-to-use Admin Dashboard App for FastAPI/Flask/Django inspired by Django Admin.","archived":false,"fork":false,"pushed_at":"2026-02-27T13:39:13.000Z","size":29157,"stargazers_count":293,"open_issues_count":0,"forks_count":33,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-02-27T15:17:20.946Z","etag":null,"topics":["admin","dashboard","django","fastadmin","fastapi","fastapi-admin","flask","ponyorm","sqlalchemy-orm","tortoise-orm"],"latest_commit_sha":null,"homepage":"https://vsdudakov.github.io/fastadmin/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vsdudakov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-02-22T05:26:33.000Z","updated_at":"2026-02-27T13:38:41.000Z","dependencies_parsed_at":"2024-08-02T18:12:57.077Z","dependency_job_id":"70a72873-e735-41f0-8db7-62e9beb0c583","html_url":"https://github.com/vsdudakov/fastadmin","commit_stats":{"total_commits":286,"total_committers":6,"mean_commits":"47.666666666666664","dds":0.07342657342657344,"last_synced_commit":"f9368ca594b2fb884c6e01052547c888dc75c84e"},"previous_names":[],"tags_count":80,"template":false,"template_full_name":null,"purl":"pkg:github/vsdudakov/fastadmin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vsdudakov%2Ffastadmin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vsdudakov%2Ffastadmin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vsdudakov%2Ffastadmin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vsdudakov%2Ffastadmin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vsdudakov","download_url":"https://codeload.github.com/vsdudakov/fastadmin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vsdudakov%2Ffastadmin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30149890,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T16:58:46.102Z","status":"ssl_error","status_checked_at":"2026-03-05T16:58:45.706Z","response_time":93,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["admin","dashboard","django","fastadmin","fastapi","fastapi-admin","flask","ponyorm","sqlalchemy-orm","tortoise-orm"],"created_at":"2024-11-03T01:03:20.098Z","updated_at":"2026-03-05T20:53:56.063Z","avatar_url":"https://github.com/vsdudakov.png","language":"Python","readme":"# Admin Dashboard for FastAPI / Flask / Django\n\n[![codecov](https://codecov.io/gh/vsdudakov/fastadmin/branch/main/graph/badge.svg?token=RNGX5HOW3T)](https://app.codecov.io/gh/vsdudakov/fastadmin)\n[![License](https://img.shields.io/github/license/vsdudakov/fastadmin)](https://github.com/vsdudakov/fastadmin/blob/master/LICENSE)\n[![PyPi](https://badgen.net/pypi/v/fastadmin)](https://pypi.org/project/fastadmin/)\n[![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/release/python-3120/)\n[![Python 3.13](https://img.shields.io/badge/python-3.13-blue.svg)](https://www.python.org/downloads/release/python-3130/)\n\n## Demo\n\n![FastAdmin demo](https://raw.githubusercontent.com/vsdudakov/fastadmin/main/docs/assets/images/demo.gif)\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://twitter.com/intent/tweet?text=Admin%20Dashboard%20For%20FastAPI\u0026url=https://github.com/vsdudakov/fastadmin\u0026hashtags=FastAPI,AdminDashboard\"\u003e\n    \u003cimg alt=\"tweet\" src=\"https://img.shields.io/twitter/url/https/twitter?label=Share%20on%20twitter\u0026style=social\" target=\"_blank\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\n\n## Introduction\n\n  \n\u003ca href='https://github.com/vsdudakov/fastadmin' target='_blank'\u003eFastAdmin\u003c/a\u003e is an easy-to-use admin dashboard for FastAPI, Django, and Flask, inspired by Django Admin.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n  \nFastAdmin is built with relationships in mind and admiration for Django Admin. Its design focuses on making it as easy as possible to configure your admin dashboard for FastAPI, Django, or Flask.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n  \nFastAdmin aims to be minimal, functional, and familiar.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n## Getting Started\n\n  \n\n\n\n\nIf you have questions beyond this documentation, feel free to \u003ca href='mailto:vsdudakov@gmail.com' target='_blank'\u003eemail us\u003c/a\u003e.\n\n\n\n\n\n\n\n\n\n\n\n  \n  \n  \n### Installation\n\n  \n\n\nFollow the steps below to set up FastAdmin:\n\n\n\n\n\n\n\n\n\n\n\n\n  \nInstall the package with pip:\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n  \n\n\n\n\nOn zsh and macOS, use quotes: \u003ccode\u003epip install 'fastadmin[fastapi,django]'\u003c/code\u003e\n\n\n\n\n\n\n\n\n\n\n  \n\n\n\n\n\n\n\n\n```bash\n\npip install fastadmin[fastapi,django]        # FastAPI with Django ORM\npip install fastadmin[fastapi,tortoise-orm]  # FastAPI with Tortoise ORM\npip install fastadmin[fastapi,pony]          # FastAPI with Pony ORM\npip install fastadmin[fastapi,sqlalchemy]    # FastAPI with SQLAlchemy (includes greenlet)\npip install fastadmin[django]                # Django with Django ORM\npip install fastadmin[django,pony]           # Django with Pony ORM\npip install fastadmin[flask,sqlalchemy]      # Flask with SQLAlchemy (includes greenlet)\n\n```\n\n\n\n\n\n\n  \nOr install with Poetry:\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n  \n\n\n\n\n\n\n\n\n```bash\n\npoetry add 'fastadmin[fastapi,django]'\npoetry add 'fastadmin[fastapi,tortoise-orm]'\npoetry add 'fastadmin[fastapi,pony]'\npoetry add 'fastadmin[fastapi,sqlalchemy]'\npoetry add 'fastadmin[django]'\npoetry add 'fastadmin[django,pony]'\npoetry add 'fastadmin[flask,sqlalchemy]'\n\n```\n\n\n\n\n\n\n  \n\n\n\n\nWhen using SQLAlchemy, the \u003ccode\u003egreenlet\u003c/code\u003e package is required (included in the \u003ccode\u003efastadmin[sqlalchemy]\u003c/code\u003e extra).\n\n\n\n\n\n\n\n\n\n\n  \nConfigure the required settings with environment variables:\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n  \n\n\n\n\nYou can add these variables to a \u003ccode\u003e.env\u003c/code\u003e file and load them with python-dotenv. See \u003ca href='https://vsdudakov.github.io/fastadmin#settings'\u003eall settings\u003c/a\u003e in the full documentation.\n\n\n\n\n\n\n\n\n\n\n  \n\n\n\n\n\n\n\n\n```bash\n\nexport ADMIN_USER_MODEL=User\nexport ADMIN_USER_MODEL_USERNAME_FIELD=username\nexport ADMIN_SECRET_KEY=secret_key\n\n```\n\n\n\n\n\n\n  \n  \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n## Quick Examples\n\n### ORM setup (User, UserAttachment, actions, widgets)\n\n#### Tortoise ORM\n\n```python\nfrom tortoise import fields\nfrom tortoise.models import Model\n\n\nclass User(Model):\n    username = fields.CharField(max_length=255, unique=True)\n    hash_password = fields.CharField(max_length=255)\n    is_superuser = fields.BooleanField(default=False)\n    is_active = fields.BooleanField(default=True)\n    avatar_url = fields.TextField(null=True)\n\n\nclass UserAttachment(Model):\n    user = fields.ForeignKeyField(\"models.User\", related_name=\"attachments\")\n    attachment_url = fields.TextField()\n```\n\n```python\nfrom fastadmin import (\n    TortoiseInlineModelAdmin,\n    TortoiseModelAdmin,\n    WidgetType,\n    action,\n    register,\n    widget_action,\n)\nfrom fastadmin.models.schemas import (\n    WidgetActionChartProps,\n    WidgetActionInputSchema,\n    WidgetActionResponseSchema,\n    WidgetActionType,\n)\nfrom .models import User, UserAttachment\n\n\nclass UserAttachmentInline(TortoiseInlineModelAdmin):\n    model = UserAttachment\n    formfield_overrides = {\n        \"attachment_url\": (WidgetType.UploadFile, {\"required\": True}),\n    }\n\n    async def upload_file(self, field_name: str, file_name: str, file_content: bytes) -\u003e str:\n        # save file to media directory or to s3/filestorage here\n        return f\"/media/{file_name}\"\n\n\n@register(User)\nclass UserAdmin(TortoiseModelAdmin):\n    list_display = (\"id\", \"username\", \"is_superuser\", \"is_active\")\n    inlines = (UserAttachmentInline,)\n\n    formfield_overrides = {\n        \"avatar_url\": (WidgetType.UploadImage, {\"required\": False}),\n    }\n\n    actions = (\"activate\", \"deactivate\")\n    widget_actions = (\"users_chart\", \"users_list\")\n\n    @action(description=\"Activate selected users\")\n    async def activate(self, ids: list[int]) -\u003e None:\n        await self.model_cls.filter(id__in=ids).update(is_active=True)\n\n    @action(description=\"Deactivate selected users\")\n    async def deactivate(self, ids: list[int]) -\u003e None:\n        await self.model_cls.filter(id__in=ids).update(is_active=False)\n\n    async def upload_file(self, field_name: str, file_name: str, file_content: bytes) -\u003e str:\n        # handle avatar_url uploads for User (and other file fields if needed)\n        return f\"/media/{file_name}\"\n\n    @widget_action(\n        widget_action_type=WidgetActionType.ChartLine,\n        widget_action_props=WidgetActionChartProps(x_field=\"x\", y_field=\"y\", series_field=\"series\"),\n        tab=\"Analytics\",\n        title=\"Users over time\",\n    )\n    async def users_chart(self, payload: WidgetActionInputSchema) -\u003e WidgetActionResponseSchema:\n        return WidgetActionResponseSchema(\n            data=[\n                {\"x\": \"2026-01-01\", \"y\": 10, \"series\": \"Active\"},\n                {\"x\": \"2026-01-02\", \"y\": 15, \"series\": \"Active\"},\n                {\"x\": \"2026-01-01\", \"y\": 3, \"series\": \"Inactive\"},\n                {\"x\": \"2026-01-02\", \"y\": 5, \"series\": \"Inactive\"},\n            ]\n        )\n\n    @widget_action(\n        widget_action_type=WidgetActionType.Action,\n        tab=\"Data\",\n        title=\"Users list\",\n        description=\"Simple action widget that returns a table of users.\",\n    )\n    async def users_list(self, payload: WidgetActionInputSchema) -\u003e WidgetActionResponseSchema:\n        return WidgetActionResponseSchema(\n            data=[\n                {\"id\": 1, \"username\": \"alice\"},\n                {\"id\": 2, \"username\": \"bob\"},\n            ]\n        )\n```\n\n#### Django ORM\n\n```python\nfrom django.db import models\n\n\nclass User(models.Model):\n    username = models.CharField(max_length=255, unique=True)\n    password = models.CharField(max_length=255)\n    is_superuser = models.BooleanField(default=False)\n    is_active = models.BooleanField(default=True)\n    avatar_url = models.ImageField(null=True)\n\n\nclass UserAttachment(models.Model):\n    user = models.ForeignKey(User, related_name=\"attachments\", on_delete=models.CASCADE)\n    attachment_url = models.FileField()\n```\n\n```python\nfrom fastadmin import (\n    DjangoInlineModelAdmin,\n    DjangoModelAdmin,\n    WidgetType,\n    action,\n    register,\n    widget_action,\n)\nfrom fastadmin.models.schemas import (\n    WidgetActionArgumentProps,\n    WidgetActionInputSchema,\n    WidgetActionProps,\n    WidgetActionResponseSchema,\n    WidgetActionType,\n)\nfrom .models import User, UserAttachment\n\n\nclass UserAttachmentInline(DjangoInlineModelAdmin):\n    model = UserAttachment\n    formfield_overrides = {\n        \"attachment_url\": (WidgetType.UploadFile, {\"required\": True}),\n    }\n\n    def upload_file(self, field_name: str, file_name: str, file_content: bytes) -\u003e str:\n        # save file to media directory or to s3/filestorage here\n        return f\"/media/{file_name}\"\n\n\n@register(User)\nclass UserAdmin(DjangoModelAdmin):\n    list_display = (\"id\", \"username\", \"is_superuser\", \"is_active\")\n    inlines = (UserAttachmentInline,)\n\n    formfield_overrides = {\n        \"avatar_url\": (WidgetType.UploadImage, {\"required\": False}),\n    }\n\n    actions = (\"activate\", \"deactivate\")\n    widget_actions = (\"users_summary\", \"users_chart\")\n\n    @action(description=\"Activate selected users\")\n    def activate(self, ids):\n        self.model_cls.objects.filter(id__in=ids).update(is_active=True)\n\n    @action(description=\"Deactivate selected users\")\n    def deactivate(self, ids):\n        self.model_cls.objects.filter(id__in=ids).update(is_active=False)\n\n    def upload_file(self, field_name: str, file_name: str, file_content: bytes) -\u003e str:\n        # handle avatar_url uploads for User (and other file fields if needed)\n        return f\"/media/{file_name}\"\n\n    @widget_action(\n        widget_action_type=WidgetActionType.Action,\n        widget_action_props=WidgetActionProps(\n            arguments=[\n                WidgetActionArgumentProps(\n                    name=\"only_active\",\n                    widget_type=WidgetType.Switch,\n                    widget_props={\"required\": False},\n                )\n            ]\n        ),\n        tab=\"Data\",\n        title=\"Users summary\",\n    )\n    def users_summary(self, payload: WidgetActionInputSchema) -\u003e WidgetActionResponseSchema:\n        qs = self.model_cls.objects.filter(is_active=True) if payload.arguments.get(\"only_active\") else self.model_cls.objects.all()\n        return WidgetActionResponseSchema(\n            data=[{\"id\": u.id, \"username\": u.username} for u in qs[:5]]\n        )\n\n    @widget_action(\n        widget_action_type=WidgetActionType.ChartLine,\n        widget_action_props=WidgetActionChartProps(x_field=\"label\", y_field=\"value\", series_field=\"series\"),\n        tab=\"Analytics\",\n        title=\"Active vs inactive users\",\n    )\n    def users_chart(self, payload: WidgetActionInputSchema) -\u003e WidgetActionResponseSchema:\n        active = self.model_cls.objects.filter(is_active=True).count()\n        inactive = self.model_cls.objects.filter(is_active=False).count()\n        return WidgetActionResponseSchema(\n            data=[\n                {\"label\": \"users\", \"value\": active, \"series\": \"active\"},\n                {\"label\": \"users\", \"value\": inactive, \"series\": \"inactive\"},\n            ]\n        )\n```\n\n#### SQLAlchemy\n\n```python\nfrom sqlalchemy import Boolean, ForeignKey, Integer, String, Text\nfrom sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine\nfrom sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship\n\nengine = create_async_engine(\"sqlite+aiosqlite:///:memory:\")\nsessionmaker = async_sessionmaker(engine, expire_on_commit=False)\n\n\nclass Base(DeclarativeBase):\n    pass\n\n\nclass User(Base):\n    __tablename__ = \"user\"\n\n    id: Mapped[int] = mapped_column(Integer, primary_key=True)\n    username: Mapped[str] = mapped_column(String(255), unique=True)\n    password: Mapped[str] = mapped_column(String(255))\n    is_superuser: Mapped[bool] = mapped_column(Boolean, default=False)\n    is_active: Mapped[bool] = mapped_column(Boolean, default=True)\n    avatar_url: Mapped[str | None] = mapped_column(Text, nullable=True)\n\n    attachments: Mapped[list[\"UserAttachment\"]] = relationship(back_populates=\"user\")\n\n\nclass UserAttachment(Base):\n    __tablename__ = \"user_attachment\"\n\n    id: Mapped[int] = mapped_column(Integer, primary_key=True)\n    user_id: Mapped[int] = mapped_column(ForeignKey(\"user.id\"))\n    attachment_url: Mapped[str] = mapped_column(Text)\n\n    user: Mapped[User] = relationship(back_populates=\"attachments\")\n```\n\n```python\nfrom sqlalchemy import update\n\nfrom fastadmin import (\n    SqlAlchemyInlineModelAdmin,\n    SqlAlchemyModelAdmin,\n    WidgetType,\n    action,\n    register,\n    widget_action,\n)\nfrom fastadmin.models.schemas import (\n    WidgetActionChartProps,\n    WidgetActionInputSchema,\n    WidgetActionResponseSchema,\n    WidgetActionType,\n)\nfrom .models import User, UserAttachment, sessionmaker\n\n\nclass UserAttachmentInline(SqlAlchemyInlineModelAdmin):\n    model = UserAttachment\n    formfield_overrides = {\n        \"attachment_url\": (WidgetType.UploadFile, {\"required\": True}),\n    }\n\n    async def upload_file(self, field_name: str, file_name: str, file_content: bytes) -\u003e str:\n        # save file to media directory or to s3/filestorage here\n        return f\"/media/{file_name}\"\n\n\n@register(User, sqlalchemy_sessionmaker=sessionmaker)\nclass UserAdmin(SqlAlchemyModelAdmin):\n    list_display = (\"id\", \"username\", \"is_superuser\", \"is_active\")\n    inlines = (UserAttachmentInline,)\n\n    formfield_overrides = {\n        \"avatar_url\": (WidgetType.UploadImage, {\"required\": False}),\n    }\n\n    actions = (\"activate\", \"deactivate\")\n    widget_actions = (\"users_chart\", \"users_list\")\n\n    @action(description=\"Activate selected users\")\n    async def activate(self, ids):\n        sm = self.get_sessionmaker()\n        async with sm() as s:\n            await s.execute(update(User).where(User.id.in_(ids)).values(is_active=True))\n            await s.commit()\n\n    @action(description=\"Deactivate selected users\")\n    async def deactivate(self, ids):\n        sm = self.get_sessionmaker()\n        async with sm() as s:\n            await s.execute(update(User).where(User.id.in_(ids)).values(is_active=False))\n            await s.commit()\n\n    async def upload_file(self, field_name: str, file_name: str, file_content: bytes) -\u003e str:\n        # handle avatar_url uploads for User (and other file fields if needed)\n        return f\"/media/{file_name}\"\n\n    @widget_action(\n        widget_action_type=WidgetActionType.ChartBar,\n        widget_action_props=WidgetActionChartProps(x_field=\"label\", y_field=\"value\", series_field=\"series\"),\n        tab=\"Analytics\",\n        title=\"Users count\",\n    )\n    async def users_chart(self, payload: WidgetActionInputSchema) -\u003e WidgetActionResponseSchema:\n        return WidgetActionResponseSchema(\n            data=[\n                {\"label\": \"users\", \"value\": 42, \"series\": \"all\"},\n            ]\n        )\n\n    @widget_action(\n        widget_action_type=WidgetActionType.Action,\n        tab=\"Data\",\n        title=\"Users list\",\n    )\n    async def users_list(self, payload: WidgetActionInputSchema) -\u003e WidgetActionResponseSchema:\n        # In a real app, fetch from the DB; here it's just a static example\n        return WidgetActionResponseSchema(\n            data=[\n                {\"id\": 1, \"username\": \"alice\"},\n                {\"id\": 2, \"username\": \"bob\"},\n            ]\n        )\n```\n\n#### Pony ORM\n\n```python\nfrom pony.orm import Database, LongStr, PrimaryKey, Required, Set\n\ndb = Database()\n\n\nclass User(db.Entity):  # type: ignore[misc]\n    _table_ = \"user\"\n    id = PrimaryKey(int, auto=True)\n    username = Required(str)\n    password = Required(str)\n    is_superuser = Required(bool, default=False)\n    is_active = Required(bool, default=True)\n    avatar_url = Required(LongStr, nullable=True)\n\n    attachments = Set(\"UserAttachment\")\n\n\nclass UserAttachment(db.Entity):  # type: ignore[misc]\n    _table_ = \"user_attachment\"\n    id = PrimaryKey(int, auto=True)\n    user = Required(User)\n    attachment_url = Required(LongStr)\n```\n\n```python\nfrom pony.orm import commit, db_session\n\nfrom fastadmin import (\n    PonyORMInlineModelAdmin,\n    PonyORMModelAdmin,\n    WidgetType,\n    action,\n    register,\n    widget_action,\n)\nfrom fastadmin.models.schemas import (\n    WidgetActionInputSchema,\n    WidgetActionResponseSchema,\n    WidgetActionType,\n)\nfrom .models import User, UserAttachment\n\n\nclass UserAttachmentInline(PonyORMInlineModelAdmin):\n    model = UserAttachment\n    formfield_overrides = {\n        \"attachment_url\": (WidgetType.UploadFile, {\"required\": True}),\n    }\n\n    def upload_file(self, field_name: str, file_name: str, file_content: bytes) -\u003e str:\n        # save file to media directory or to s3/filestorage here\n        return f\"/media/{file_name}\"\n\n\n@register(User)\nclass UserAdmin(PonyORMModelAdmin):\n    list_display = (\"id\", \"username\", \"is_superuser\", \"is_active\")\n    inlines = (UserAttachmentInline,)\n\n    formfield_overrides = {\n        \"avatar_url\": (WidgetType.UploadImage, {\"required\": False}),\n    }\n\n    actions = (\"activate\", \"deactivate\")\n    widget_actions = (\"users_list\", \"users_chart\")\n\n    @action(description=\"Activate selected users\")\n    @db_session\n    def activate(self, ids):\n        for u in User.select(lambda o: o.id in ids):\n            u.is_active = True\n        commit()\n\n    @action(description=\"Deactivate selected users\")\n    @db_session\n    def deactivate(self, ids):\n        for u in User.select(lambda o: o.id in ids):\n            u.is_active = False\n        commit()\n\n    def upload_file(self, field_name: str, file_name: str, file_content: bytes) -\u003e str:\n        # handle avatar_url uploads for User (and other file fields if needed)\n        return f\"/media/{file_name}\"\n\n    @widget_action(widget_action_type=WidgetActionType.Action, tab=\"Data\", title=\"Users list\")\n    @db_session\n    def users_list(self, payload: WidgetActionInputSchema) -\u003e WidgetActionResponseSchema:\n        return WidgetActionResponseSchema(\n            data=[{\"id\": u.id, \"username\": u.username} for u in User.select()[:5]]\n        )\n\n    @widget_action(widget_action_type=WidgetActionType.ChartPie, tab=\"Analytics\", title=\"Users by activity\")\n    @db_session\n    def users_chart(self, payload: WidgetActionInputSchema) -\u003e WidgetActionResponseSchema:\n        active = User.select(lambda u: u.is_active).count()\n        inactive = User.select(lambda u: not u.is_active).count()\n        return WidgetActionResponseSchema(\n            data=[\n                {\"type\": \"active\", \"value\": active},\n                {\"type\": \"inactive\", \"value\": inactive},\n            ]\n        )\n```\n\n### Request and user context in admin methods\n\nYou can access the current **request** and **authenticated user** in your admin methods via `self.request` and `self.user`. This works the same way for both `ModelAdmin` and `InlineModelAdmin`.\n\n```python\nfrom fastadmin import TortoiseModelAdmin, register\nfrom .models import Event\n\n\n@register(Event)\nclass EventAdmin(TortoiseModelAdmin):\n    async def has_change_permission(self, user_id: int | None = None) -\u003e bool:\n        # you can either use user_id to load the user from the DB,\n        # or rely on self.user – the current authenticated admin user\n        if self.user and self.user.get(\"is_superuser\"):\n            return True\n        return False\n\n    async def save_model(self, id: int | None, payload: dict) -\u003e dict:\n        # self.request is the current HTTP request\n        if self.request and getattr(self.request, \"client\", None):\n            payload[\"changed_from_ip\"] = getattr(\n              self.request.client,\n              \"host\",\n              None,\n            )\n        return await super().save_model(id, payload)\n```\n\nInline admins get the same properties (`self.user`, `self.request`), so you can reuse this pattern in inline-specific hooks like `save_model` or custom `action` / `widget_action` methods.\n\n### Framework integration (register User admin)\n\n#### FastAPI\n\n```python\nfrom fastapi import FastAPI\n\nfrom fastadmin import fastapi_app as admin_app\n\nimport myapp.admin  # import to register User admin\n\napp = FastAPI()\n\napp.mount(\"/admin\", admin_app)\n```\n\n#### Django\n\n```python\nfrom django.urls import path\n\nfrom fastadmin import get_django_admin_urls as get_admin_urls\nfrom fastadmin.settings import settings\n\nimport myapp.admin  # imports @register(User)\n\nurlpatterns = [\n    path(f\"{settings.ADMIN_PREFIX}/\", get_admin_urls()),\n]\n```\n\n#### Flask\n\n```python\nfrom flask import Flask\n\nfrom fastadmin import flask_app as admin_app\nfrom fastadmin.settings import settings\n\nimport myapp.admin  # imports @register(User)\n\napp = Flask(__name__)\n\napp.register_blueprint(admin_app, url_prefix=f\"/{settings.ADMIN_PREFIX}\")\n```\n## Documentation\n\nFull documentation is available at [vsdudakov.github.io/fastadmin](https://vsdudakov.github.io/fastadmin).\n\n## License\n\nThis project is licensed under the MIT License — see the [LICENSE](https://github.com/vsdudakov/fastadmin/blob/main/LICENSE) file for details.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvsdudakov%2Ffastadmin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvsdudakov%2Ffastadmin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvsdudakov%2Ffastadmin/lists"}