mirror of
https://github.com/fastapi-admin/fastapi-admin.git
synced 2025-08-16 11:54:15 +08:00
205 lines
7.2 KiB
Python
205 lines
7.2 KiB
Python
import io
|
|
from typing import Type
|
|
|
|
import xlsxwriter
|
|
from fastapi import APIRouter, Depends
|
|
from fastapi.responses import JSONResponse
|
|
from starlette.requests import Request
|
|
from starlette.responses import StreamingResponse
|
|
from starlette.status import HTTP_409_CONFLICT
|
|
from tortoise import Model
|
|
from tortoise.contrib.pydantic import pydantic_model_creator
|
|
from tortoise.exceptions import IntegrityError
|
|
from tortoise.fields import ManyToManyRelation
|
|
|
|
from ..common import handle_m2m_fields_create_or_update
|
|
from ..depends import (
|
|
QueryItem,
|
|
admin_log_create,
|
|
admin_log_delete,
|
|
admin_log_update,
|
|
create_checker,
|
|
delete_checker,
|
|
get_current_user,
|
|
get_model,
|
|
get_query,
|
|
has_create_permission,
|
|
has_delete_permission,
|
|
has_read_permission,
|
|
has_update_permission,
|
|
parse_body,
|
|
read_checker,
|
|
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
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/{resource}/export")
|
|
async def export(resource: str, query: QueryItem = Depends(get_query), model=Depends(get_model)):
|
|
qs = model.all()
|
|
if query.where:
|
|
qs = qs.filter(**query.where)
|
|
resource = await app.get_resource(resource)
|
|
result = await qs
|
|
creator = pydantic_model_creator(
|
|
model, include=resource.resource_fields.keys(), exclude=model._meta.m2m_fields
|
|
)
|
|
data = map(lambda x: creator.from_orm(x).dict(), result)
|
|
|
|
output = io.BytesIO()
|
|
workbook = xlsxwriter.Workbook(output)
|
|
worksheet = workbook.add_worksheet()
|
|
for row, item in enumerate(data):
|
|
col = 0
|
|
for k, v in item.items():
|
|
if row == 0:
|
|
worksheet.write(row, col, k)
|
|
worksheet.write(row + 1, col, v)
|
|
col += 1
|
|
|
|
workbook.close()
|
|
output.seek(0)
|
|
|
|
return StreamingResponse(output)
|
|
|
|
|
|
@router.post("/{resource}/import")
|
|
async def import_data(request: Request, model: Type[Model] = Depends(get_model)):
|
|
items = await request.json()
|
|
objs = []
|
|
for item in items:
|
|
obj = model(**item)
|
|
objs.append(obj)
|
|
try:
|
|
await model.bulk_create(objs)
|
|
return {"success": True, "data": len(objs)}
|
|
except IntegrityError as e:
|
|
return JSONResponse(status_code=HTTP_409_CONFLICT, content=dict(msg=f"Import Error,{e}"))
|
|
|
|
|
|
@router.get("/{resource}", dependencies=[Depends(read_checker)])
|
|
async def get_resource(
|
|
resource: str, query: QueryItem = Depends(get_query), model=Depends(get_model)
|
|
):
|
|
menu = app.model_menu_mapping[resource]
|
|
qs = model.all()
|
|
for filter_ in menu.custom_filters:
|
|
qs = filter_.get_queryset(qs)
|
|
if 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:
|
|
if v == -1:
|
|
qs = qs.order_by(f"-{k}")
|
|
elif v == 1:
|
|
qs = qs.order_by(k)
|
|
resource = await app.get_resource(resource)
|
|
result = await qs.limit(query.size).offset((query.page - 1) * query.size)
|
|
creator = pydantic_model_creator(
|
|
model, include=resource.resource_fields.keys(), exclude=model._meta.m2m_fields
|
|
)
|
|
data = []
|
|
for item in result:
|
|
item_dict = creator.from_orm(item).dict()
|
|
item_dict["_rowVariant"] = item_dict.pop("rowVariant", None)
|
|
item_dict["_cellVariants"] = item_dict.pop("cellVariants", None)
|
|
data.append(item_dict)
|
|
return GetManyOut(total=await qs.count(), data=data)
|
|
|
|
|
|
@router.get("/{resource}/form", dependencies=[Depends(read_checker)])
|
|
async def form(resource: str,):
|
|
resource = await app.get_resource(
|
|
resource, exclude_pk=True, exclude_m2m_field=False, exclude_actions=True
|
|
)
|
|
return resource.dict(by_alias=True, exclude_unset=True)
|
|
|
|
|
|
@router.get("/{resource}/grid", dependencies=[Depends(read_checker)])
|
|
async def grid(resource: str, user=Depends(get_current_user)):
|
|
resource = await app.get_resource(resource)
|
|
resource = resource.dict(by_alias=True, exclude_unset=True)
|
|
resource["fields"]["_actions"] = {
|
|
"delete": await has_delete_permission(resource, user),
|
|
"edit": await has_update_permission(resource, user),
|
|
"toolbar": {"create": await has_create_permission(resource, user)},
|
|
}
|
|
return resource
|
|
|
|
|
|
@router.get("/{resource}/view", dependencies=[Depends(read_checker)])
|
|
async def view(resource: str,):
|
|
resource = await app.get_resource(resource)
|
|
return resource.dict(by_alias=True, exclude_unset=True)
|
|
|
|
|
|
@router.post(
|
|
"/{resource}/bulk/delete", dependencies=[Depends(delete_checker), Depends(admin_log_delete)]
|
|
)
|
|
async def bulk_delete(bulk_in: BulkIn, model=Depends(get_model)):
|
|
await model.filter(pk__in=bulk_in.pk_list).delete()
|
|
return {"success": True}
|
|
|
|
|
|
@router.delete(
|
|
"/{resource}/{id}", dependencies=[Depends(delete_checker), Depends(admin_log_delete)]
|
|
)
|
|
async def delete_one(id: int, model=Depends(get_model)):
|
|
await model.filter(pk=id).delete()
|
|
return {"success": True}
|
|
|
|
|
|
@router.put("/{resource}/{id}", dependencies=[Depends(update_checker), Depends(admin_log_update)])
|
|
async def update_one(id: int, parsed=Depends(parse_body), model=Depends(get_model)):
|
|
body, resource_fields = parsed
|
|
m2m_fields = model._meta.m2m_fields
|
|
try:
|
|
obj = await handle_m2m_fields_create_or_update(
|
|
body, m2m_fields, model, app.user_model, False, id
|
|
)
|
|
except IntegrityError as e:
|
|
return JSONResponse(status_code=HTTP_409_CONFLICT, content=dict(msg=f"Update Error,{e}"))
|
|
creator = pydantic_model_creator(model, include=resource_fields, exclude=m2m_fields)
|
|
return creator.from_orm(obj).dict()
|
|
|
|
|
|
@router.post("/{resource}", dependencies=[Depends(create_checker), Depends(admin_log_create)])
|
|
async def create_one(parsed=Depends(parse_body), model=Depends(get_model)):
|
|
body, resource_fields = parsed
|
|
m2m_fields = model._meta.m2m_fields
|
|
creator = pydantic_model_creator(model, include=resource_fields, exclude=m2m_fields)
|
|
try:
|
|
obj = await handle_m2m_fields_create_or_update(body, m2m_fields, model, app.user_model)
|
|
except IntegrityError as e:
|
|
return JSONResponse(status_code=HTTP_409_CONFLICT, content=dict(msg=f"Create Error,{e}"))
|
|
return creator.from_orm(obj).dict()
|
|
|
|
|
|
@router.get("/{resource}/{id}", dependencies=[Depends(read_checker)])
|
|
async def get_one(id: int, resource: str, model=Depends(get_model)):
|
|
obj = await get_object_or_404(model, pk=id) # type:Model
|
|
m2m_fields = model._meta.m2m_fields
|
|
resource = await app.get_resource(resource, exclude_m2m_field=False)
|
|
include = resource.resource_fields.keys()
|
|
creator = pydantic_model_creator(model, include=include, exclude=m2m_fields)
|
|
ret = creator.from_orm(obj).dict()
|
|
for m2m_field in m2m_fields:
|
|
if m2m_field in include:
|
|
relate_model = getattr(obj, m2m_field) # type:ManyToManyRelation
|
|
ids = await relate_model.all().values_list(relate_model.remote_model._meta.pk_attr)
|
|
ret[m2m_field] = list(map(lambda x: x[0], ids))
|
|
ret["__str__"] = str(obj)
|
|
return ret
|