mirror of
https://github.com/fastapi-admin/fastapi-admin.git
synced 2025-08-18 13:16:33 +08:00
new project
This commit is contained in:
377
docs/content.md
377
docs/content.md
@ -1,377 +0,0 @@
|
||||
## Builtin Auth And Permissions Control
|
||||
|
||||
You should inherit `fastapi_admin.models.AbstractUser`,`fastapi_admin.models.AbstractPermission`,`fastapi_admin.models.AbstractRole` and add extra fields.
|
||||
|
||||
```python
|
||||
from fastapi_admin.models import AbstractUser, AbstractPermission, AbstractRole
|
||||
|
||||
class AdminUser(AbstractUser):
|
||||
is_active = fields.BooleanField(default=False, description='Is Active')
|
||||
is_superuser = fields.BooleanField(default=False, description='Is Superuser')
|
||||
status = fields.IntEnumField(Status, description='User Status')
|
||||
created_at = fields.DatetimeField(auto_now_add=True)
|
||||
updated_at = fields.DatetimeField(auto_now=True)
|
||||
|
||||
class Permission(AbstractPermission):
|
||||
"""
|
||||
must inheritance AbstractPermission
|
||||
"""
|
||||
|
||||
|
||||
class Role(AbstractRole):
|
||||
"""
|
||||
must inheritance AbstractRole
|
||||
"""
|
||||
|
||||
|
||||
class AdminLog(AbstractAdminLog):
|
||||
"""
|
||||
must inheritance AbstractAdminLog
|
||||
"""
|
||||
```
|
||||
|
||||
And set `permission=True` to active it:
|
||||
|
||||
```python
|
||||
await admin_app.init(
|
||||
...
|
||||
permission=True,
|
||||
site=Site(
|
||||
...
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
And createsuperuser:
|
||||
|
||||
```shell
|
||||
> fastapi-admin -h
|
||||
usage: fastapi-admin [-h] -c CONFIG [--version] {createsuperuser} ...
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-c CONFIG, --config CONFIG
|
||||
Tortoise-orm config dict import path,like settings.TORTOISE_ORM.
|
||||
--version, -V show the version
|
||||
|
||||
subcommands:
|
||||
{createsuperuser}
|
||||
```
|
||||
|
||||
Before you use this command - remember that you have to define your own user model that inherits from fastapi-admin `AbstractUser`.
|
||||
eg.
|
||||
```python
|
||||
from fastapi_admin.models import AbstractUser
|
||||
from tortoise import fields
|
||||
|
||||
class User(AbstractUser):
|
||||
id = fields.BigIntField(pk=True)
|
||||
|
||||
```
|
||||
|
||||
Here's an example of how createsuperuser command can look like:
|
||||
|
||||
```shell
|
||||
fastapi-admin -c "db.DB_CONFIG" createsuperuser -u User
|
||||
```
|
||||
The code above assumes that in your module's dir you have an `db.py` file in which there's, or that you provide a correct path on your own.
|
||||
`-c` flag proceeds the path to your database config dict. It can look something like this.
|
||||
|
||||
```python
|
||||
# db.py file
|
||||
import os
|
||||
|
||||
DB_CONFIG: dict = {
|
||||
"connections": {
|
||||
"default": {
|
||||
"engine": "tortoise.backends.asyncpg",
|
||||
"credentials": {
|
||||
"host": os.environ.get("DB_HOST", "localhost"),
|
||||
"port": os.environ.get("DB_PORT", 5432),
|
||||
"user": os.environ.get("DB_USER", "user"),
|
||||
"password": os.environ.get("DB_PASSWORD", "secret_pass"),
|
||||
"database": os.environ.get("DB_DATABASE_NAME", "db"),
|
||||
},
|
||||
}
|
||||
# alternatively, probably only for testing purposes
|
||||
# "default": "sqlite://db.sqlite3",
|
||||
},
|
||||
"apps": {"models": {"models": ["app.db_models"]}},
|
||||
}
|
||||
```
|
||||
[Read more about configs and initialization in tortoise orm](https://tortoise-orm.readthedocs.io/en/latest/setup.html?highlight=config#tortoise.Tortoise.init)
|
||||
|
||||
After `-u` you can tell fastapi-admin which model inherits from AdminUser.
|
||||
|
||||
## Custom Login
|
||||
|
||||
You can write your own login view logic:
|
||||
|
||||
```python
|
||||
await admin_app.init(
|
||||
...,
|
||||
login_view="examples.routes.login"
|
||||
)
|
||||
```
|
||||
|
||||
And must return json like:
|
||||
|
||||
```json
|
||||
{
|
||||
"user": {
|
||||
"username": "admin",
|
||||
"is_superuser": false,
|
||||
"avatar": "https://avatars2.githubusercontent.com/u/13377178?s=460&u=d150d522579f41a52a0b3dd8ea997e0161313b6e&v=4"
|
||||
},
|
||||
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.HSlcYkOEQewxyPuaqcVwCcw_wkbLB50Ws1-ZxfPoLAQ"
|
||||
}
|
||||
```
|
||||
|
||||
## Enum Support
|
||||
|
||||
When you define a enum field of tortoise-orm,like `IntEnumField`,you can
|
||||
inherit `fastapi_admin.enums.EnumMixin` and impl `choices()` method,
|
||||
FastAPI-Admin will auto read and display and render a `select` widget in
|
||||
front.
|
||||
|
||||
```python
|
||||
class Status(EnumMixin, IntEnum):
|
||||
on = 1
|
||||
off = 2
|
||||
|
||||
@classmethod
|
||||
def choices(cls):
|
||||
return {
|
||||
cls.on: 'ON',
|
||||
cls.off: 'OFF'
|
||||
}
|
||||
```
|
||||
|
||||
## Help Text
|
||||
|
||||
FastAPI-Admin will auto read `description` defined in tortoise-orm model
|
||||
`Field` and display in front with form help text.
|
||||
|
||||
## ForeignKeyField Support
|
||||
|
||||
If `ForeignKeyField` is not passed in `menu.raw_id_fields`,FastAPI-Admin
|
||||
will get all related objects and display `select` in front with
|
||||
`Model.__str__`.
|
||||
|
||||
## ManyToManyField Support
|
||||
|
||||
FastAPI-Admin will render `ManyToManyField` with multiple `select` in
|
||||
`form` edit with `Model.__str__`.
|
||||
|
||||
## JSONField Render
|
||||
|
||||
FastAPI-Admin will render `JSONField` with `jsoneditor` as beauty
|
||||
interface.
|
||||
|
||||
## Search Fields
|
||||
|
||||
Defined `menu.search_fields` in `menu` will render a search form by
|
||||
fields.
|
||||
|
||||
## Xlsx Export
|
||||
|
||||
FastAPI-Admin can export searched data to excel file when define
|
||||
`export=True` in `menu`.
|
||||
|
||||
## Bulk Actions
|
||||
|
||||
Current FastAPI-Admin supports builtin bulk action `delete_all`,if you
|
||||
want to write your own bulk actions:
|
||||
|
||||
1. pass `bulk_actions` in `Menu`,example:
|
||||
|
||||
```python
|
||||
Menu(
|
||||
...
|
||||
bulk_actions=[{
|
||||
'value': 'delete', # this is fastapi router path param.
|
||||
'text': 'delete_all', # this will show in front.
|
||||
}]
|
||||
)
|
||||
```
|
||||
|
||||
2. write fastapi route,example:
|
||||
|
||||
```python
|
||||
from fastapi_admin.schemas import BulkIn
|
||||
from fastapi_admin.factory import app as admin_app
|
||||
|
||||
@admin_app.post(
|
||||
'/rest/{resource}/bulk/delete' # `delete` is defined in Menu before.
|
||||
)
|
||||
async def bulk_delete(
|
||||
bulk_in: BulkIn,
|
||||
model=Depends(get_model)
|
||||
):
|
||||
await model.filter(pk__in=bulk_in.pk_list).delete()
|
||||
return {'success': True}
|
||||
```
|
||||
|
||||
## Default Menus
|
||||
|
||||
Default, FastAPI-Admin provide default menus by your models, without
|
||||
doing tedious works. Therefore you do not need to fill the optional argument `menus` in Site definition.
|
||||
|
||||
|
||||
## Custom Menus
|
||||
You can define a custom menu that'll be used by fastapi-admin. Here's an example of how that might look.
|
||||
|
||||
```python
|
||||
|
||||
menus = [
|
||||
Menu(name="Home", url="/", icon="fa fa-home"),
|
||||
Menu(
|
||||
name="Content",
|
||||
children=[
|
||||
Menu(name="Category", url="/rest/Category", icon="fa fa-list", search_fields=("slug",)),
|
||||
Menu(name="Config", url="/rest/Config", icon="fa fa-gear", import_=True, search_fields=("key",)),
|
||||
Menu(name="Product", url="/rest/Product", icon="fa fa-table", search_fields=("name",)),
|
||||
],
|
||||
),
|
||||
Menu(
|
||||
name="External",
|
||||
children=[
|
||||
Menu(name="Github", url="https://github.com/long2ice/fastapi-admin", icon="fa fa-github", external=True),
|
||||
],
|
||||
),
|
||||
Menu(
|
||||
name="Auth",
|
||||
children=[
|
||||
Menu(name="User", url="/rest/User", icon="fa fa-user", search_fields=("username",),),
|
||||
Menu(name="Role", url="/rest/Role", icon="fa fa-group", ),
|
||||
Menu(name="Permission", url="/rest/Permission", icon="fa fa-user-plus", ),
|
||||
Menu(name="Logout", url="/logout", icon="fa fa-lock", ),
|
||||
Menu(
|
||||
name="AdminLog",
|
||||
url="/rest/AdminLog",
|
||||
icon="fa fa-align-left",
|
||||
search_fields=("action", "admin", "model"),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
```
|
||||
Each menu can either be a single element menu that'll only link to a given resource, or it can be a gathering of multiple links, that you add by using the `children` optional argument.
|
||||
|
||||
`children` should be a list of `Menu` objects.
|
||||
|
||||
Now that you have your menus you can use them during the app initialization.
|
||||
|
||||
```python
|
||||
|
||||
menus = ... # look at the code above. You can define it here or in separate file to make things neat
|
||||
|
||||
@app.on_event("startup")
|
||||
async def start_up():
|
||||
await admin_app.init( # nosec
|
||||
admin_secret="test",
|
||||
permission=True,
|
||||
admin_log=True,
|
||||
site=Site(
|
||||
name="FastAPI-Admin DEMO",
|
||||
login_footer="FASweTAPI ADMIN - FastAPI Admin Dashboard",
|
||||
login_description="FastAPI Admin Dashboard",
|
||||
locale="en-US",
|
||||
locale_switcher=True,
|
||||
theme_switcher=True,
|
||||
menus=menus
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
## Table Variant
|
||||
|
||||
You can define `RowVariant` and `CellVariants` in `computed` of `tortoise-orm`, which will effect table rows and cells variant.
|
||||
|
||||
```python
|
||||
class User(AbstractUser):
|
||||
last_login = fields.DatetimeField(description="Last Login", default=datetime.datetime.now)
|
||||
avatar = fields.CharField(max_length=200, default="")
|
||||
intro = fields.TextField(default="")
|
||||
created_at = fields.DatetimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.pk}#{self.username}"
|
||||
|
||||
def rowVariant(self) -> str:
|
||||
if not self.is_active:
|
||||
return "warning"
|
||||
return ""
|
||||
|
||||
def cellVariants(self) -> dict:
|
||||
if self.is_active:
|
||||
return {
|
||||
"intro": "info",
|
||||
}
|
||||
return {}
|
||||
|
||||
class PydanticMeta:
|
||||
computed = ("rowVariant", "cellVariants")
|
||||
```
|
||||
|
||||
## Admin log
|
||||
|
||||
You can log each admin action like `delete`,`create` and `update`,just set `admin_log=True` in `admin_app.init()` and just create a model in your app that inherits from `fastapi_admin.models.AbstractAdminLog`.
|
||||
|
||||
## Import from excel
|
||||
|
||||
You can enable `import` by set `import_=True` in `Menu` definition, and data format must same as `Model` fields.
|
||||
|
||||
## Custom filters
|
||||
|
||||
There are two kinds of filters named `Filter` and `SearchFilter`.
|
||||
|
||||
`Filter` use to filter view list default, and `SearchFilter` add a custom search input in front.
|
||||
|
||||
To use `Filter` you should only inherit `fastapi_admin.filters.Filter` then implement `get_queryset`, for example:
|
||||
|
||||
```py
|
||||
from fastapi_admin.filters import Filter
|
||||
|
||||
class CustomFilter(Filter):
|
||||
@classmethod
|
||||
def get_queryset(cls, queryset: QuerySet) -> QuerySet:
|
||||
return queryset.filter(~Q(key="test"))
|
||||
```
|
||||
|
||||
Then add it to `Menu.custom_filters`.
|
||||
|
||||
```py
|
||||
Menu(
|
||||
name="Config",
|
||||
url="/rest/Config",
|
||||
icon="fa fa-gear",
|
||||
import_=True,
|
||||
search_fields=("key",),
|
||||
custom_filters=[CustomFilter],
|
||||
)
|
||||
```
|
||||
|
||||
And to use `SearchFilter`, like `Filter` but inherit `fastapi_admin.filters.SearchFilter`, note that you show register it by `register_filter`, for example:
|
||||
|
||||
```py
|
||||
from fastapi_admin.filters import SearchFilter, register_filter
|
||||
from fastapi_admin.site import Field
|
||||
|
||||
@register_filter
|
||||
class LikeFilter(SearchFilter):
|
||||
@classmethod
|
||||
def get_queryset(cls, queryset: QuerySet, value: Any) -> QuerySet:
|
||||
return queryset.filter(name__icontains=value)
|
||||
|
||||
@classmethod
|
||||
async def get_field(cls) -> Field:
|
||||
return Field(label="NameLike", type="text")
|
||||
|
||||
@classmethod
|
||||
def get_name(cls) -> str:
|
||||
return "filter"
|
||||
```
|
||||
|
||||
`get_name` must return an unque `name` for all `SearchFilter` and `get_field` should return a `Field` instance.
|
@ -1,44 +0,0 @@
|
||||
## Import app
|
||||
|
||||
First of all suppose you have a fastapi+tortoise-orm project and running normally, then first you should is import admin app from `fastapi-admin` and mount in root fastapi app.
|
||||
|
||||
```python hl_lines="6"
|
||||
from fastapi_admin.factory import app as admin_app
|
||||
|
||||
def create_app():
|
||||
fast_app = FastAPI(debug=False)
|
||||
register_tortoise(fast_app, config=TORTOISE_ORM)
|
||||
fast_app.mount("/admin", admin_app)
|
||||
return fast_app
|
||||
|
||||
|
||||
app = create_app()
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run("main:app", port=8000, debug=False, reload=False, lifespan="on")
|
||||
|
||||
```
|
||||
|
||||
Now you can visit `http://127.0.0.1:8000/admin/docs` see all restful api comes from fastapi-admin.
|
||||
|
||||
## Init App
|
||||
|
||||
After mount admin app, you should init app now. Pay attention to that you should init admin app in fastapi `startup` event instead of run it directly.
|
||||
|
||||
```python
|
||||
@app.on_event("startup")
|
||||
async def start_up():
|
||||
await admin_app.init( # nosec
|
||||
admin_secret="test",
|
||||
permission=True,
|
||||
admin_log=True,
|
||||
site=Site(
|
||||
name="FastAPI-Admin DEMO",
|
||||
login_footer="FASweTAPI ADMIN - FastAPI Admin Dashboard",
|
||||
login_description="FastAPI Admin Dashboard",
|
||||
locale="en-US",
|
||||
locale_switcher=True,
|
||||
theme_switcher=True,
|
||||
),
|
||||
)
|
||||
```
|
Reference in New Issue
Block a user