Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	examples/main.py
This commit is contained in:
long2ice
2020-10-19 18:52:03 +08:00
11 changed files with 92 additions and 25 deletions

View File

@@ -2,6 +2,11 @@
## 0.3
### 0.3.2
- Hide menu when no children menu.
- Add custom filters.
### 0.3.1
- Auto register permission.

28
examples/filters.py Normal file
View File

@@ -0,0 +1,28 @@
from typing import Any
from tortoise import QuerySet
from tortoise.query_utils import Q
from fastapi_admin.filters import Filter, SearchFilter, register_filter
from fastapi_admin.site import Field
class CustomFilter(Filter):
@classmethod
def get_queryset(cls, queryset: QuerySet) -> QuerySet:
return queryset.filter(~Q(key="test"))
@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"

View File

@@ -3,14 +3,11 @@ import os
import uvicorn
from fastapi import Depends, FastAPI
from starlette.middleware.cors import CORSMiddleware
from starlette.requests import Request
from starlette.templating import Jinja2Templates
from tortoise.contrib.fastapi import register_tortoise
from tortoise.contrib.pydantic import pydantic_queryset_creator
from tortoise.exceptions import IntegrityError
from examples.common import get_client_ip
from examples.models import Visitor
from examples.filters import CustomFilter, LikeFilter
from fastapi_admin.depends import get_model
from fastapi_admin.factory import app as admin_app
from fastapi_admin.schemas import BulkIn
@@ -56,18 +53,6 @@ def create_app():
app = create_app()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
ip = get_client_ip(request)
v = await Visitor.get_or_none(ip=ip)
try:
await v.save() if v else await Visitor.create(ip=ip)
except IntegrityError:
pass
response = await call_next(request)
return response
@app.on_event("startup")
async def start_up():
await admin_app.init( # nosec
@@ -90,7 +75,7 @@ async def start_up():
name="Category",
url="/rest/Category",
icon="fa fa-list",
search_fields=("slug",),
search_fields=("slug", LikeFilter),
),
Menu(
name="Config",
@@ -98,6 +83,7 @@ async def start_up():
icon="fa fa-gear",
import_=True,
search_fields=("key",),
custom_filters=[CustomFilter],
),
Menu(
name="Product",
@@ -105,9 +91,6 @@ async def start_up():
icon="fa fa-table",
search_fields=("name",),
),
Menu(
name="Visitor", url="/rest/Visitor", icon="fa fa-child", exclude=("ip",)
),
],
),
Menu(

View File

@@ -5,4 +5,4 @@ def version():
# with open("pyproject.toml") as f:
# ret = re.findall(r'version = "(\d+\.\d+\.\d+)"', f.read())
# return ret[0]
return "0.3.1"
return "0.3.2"

View File

@@ -1,5 +1,4 @@
import json
from typing import Optional
import jwt
from fastapi import Depends, HTTPException, Path, Query

View File

@@ -8,6 +8,7 @@ from tortoise import Model
from . import enums
from .common import get_all_models, import_obj, pwd_context
from .exceptions import exception_handler
from .filters import SearchFilter
from .models import AbstractAdminLog, AbstractPermission, AbstractRole, AbstractUser
from .schemas import LoginIn
from .shortcuts import get_object_or_404
@@ -244,6 +245,11 @@ class AdminApp(FastAPI):
sort_fields = menu.sort_fields
fields = {}
pk = name = pk_field.get("name")
# CustomSearchFilters
for search_filter in filter(
lambda x: type(x).__name__ == "type" and issubclass(x, SearchFilter), search_fields
):
search_fields_ret[search_filter.get_name()] = await search_filter.get_field()
if not exclude_pk and not self._exclude_field(resource, name):
field = Field(
label=pk_field.get("name").title(),

36
fastapi_admin/filters.py Normal file
View File

@@ -0,0 +1,36 @@
from typing import Any
from tortoise import QuerySet
from fastapi_admin.site import Field
_search_filters = {}
class Filter:
@classmethod
def get_queryset(cls, queryset: QuerySet) -> QuerySet:
raise NotImplementedError
class SearchFilter:
@classmethod
def get_queryset(cls, queryset: QuerySet, option: Any) -> QuerySet:
raise NotImplementedError
@classmethod
async def get_field(cls) -> Field:
raise NotImplementedError
@classmethod
def get_name(cls) -> str:
raise NotImplementedError
def register_filter(cls: SearchFilter):
_search_filters[cls.get_name()] = cls
return cls
def get_filter_by_name(name: str):
return _search_filters.get(name)

View File

@@ -27,6 +27,7 @@ from ..depends import (
update_checker,
)
from ..factory import app
from ..filters import get_filter_by_name
from ..responses import GetManyOut
from ..schemas import BulkIn
from ..shortcuts import get_object_or_404
@@ -83,8 +84,15 @@ async def get_resource(
):
menu = app.model_menu_mapping[resource]
qs = model.all()
for filter_ in menu.custom_filters:
qs = filter_.get_queryset(qs)
if query.where:
qs = qs.filter(**query.where)
for name, value in query.where.items():
filter_cls = get_filter_by_name(name)
if filter_cls:
qs = filter_cls.get_queryset(qs, value)
else:
qs = qs.filter(**{name: value})
sort = query.sort
for k, v in sort.items():
if k in menu.sort_fields:

View File

@@ -30,4 +30,5 @@ async def site(user_id=Depends(jwt_optional)):
model = child.url.split("/")[-1]
if not await check_has_permission(user, model):
site_copy.menus[index].children.remove(child)
site_copy.menus = list(filter(lambda x: x.children != [], site_copy.menus))
return site_copy.dict(by_alias=True, exclude_unset=True)

View File

@@ -19,7 +19,7 @@ class Menu(BaseModel):
# raw id fields
raw_id_fields: Optional[Tuple[str, ...]] = tuple()
# searchable fields
search_fields: Optional[Tuple[str, ...]] = tuple()
search_fields = tuple()
# sortable fields
sort_fields: Optional[Tuple[str, ...]] = tuple()
# define field type,like select,radiolist,text,date
@@ -31,6 +31,7 @@ class Menu(BaseModel):
import_: bool = False
actions: Optional[Dict]
bulk_actions: List[Dict] = [{"value": "delete", "text": "delete_all"}]
custom_filters: List = []
Menu.update_forward_refs()

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "fastapi-admin"
version = "0.3.1"
version = "0.3.2"
description = "Fast Admin Dashboard based on fastapi and tortoise-orm."
authors = ["long2ice <long2ice@gmail.com>"]
license = "Apache-2.0"