amisadmin / fastapi-amis-admin

FastAPI-Amis-Admin is a high-performance, efficient and easily extensible FastAPI admin framework. Inspired by django-admin, and has as many powerful functions as django-admin.
http://docs.amis.work
Apache License 2.0
961 stars 142 forks source link

Actions: custom PK (except id) is not passed to ModelAction.handle() #165

Open prostospirit opened 3 months ago

prostospirit commented 3 months ago

For example, I have the following model:

class Promoaction(SQLModel, table=True):
    __tablename__ = "promoaction"

    promoaction_id: int
    name: str

I have the next admin:

class ActionAdmin(admin.ModelAdmin):
    page_schema = 'Promoaction Management'
    model = Promoaction
    pk_name = 'promoaction_id'     # set custom pk

    admin_action_maker = [
        lambda self: LoadMailsByAction(self, name="load_mails_action", label="Load Mails", flags=["item", "load_mails"]),
    ]

and i have the next action:

class LoadMailsByAction(admin.ModelAction):
    action = ActionType.Dialog(tooltip="Upload mails by promoaction", icon="fa fa-paper-plane", level=LevelEnum.warning,
                               dialog=Dialog())

    class schema(BaseModel):
        promoaction_ids: list[int] = Field(None, title="Enter promoaction IDs")

    async def handle(self, request: Request, item_id: List[str], data: Optional[SchemaUpdateT], **kwargs) -> BaseApiOut[Any]:
        items: list[Action] = await self.admin.fetch_items(*item_id)
        responses: dict[int, str] = {}
        for promoaction_id in data.promoaction_ids:
            result = update_mails_without_filesystem(action_id=promoaction_id)
            responses.update({promoaction_id: result})
        return BaseApiOut(data=responses)

I expected that item_id would be passed to the handle function in the item_id parameter, but it does not arrive.

Same time in the browser console I see the following request:

http://...:8000/admin/my_app/PromoactionAdmin/LoadMailsByAction/api?item_id= (item_id is empty)

What needs to be done for this to work correctly? I tried to find this configuration settings but failed. Thanks in advance for your answer and for the project!

mmmcorpsvit commented 3 months ago

i think, Request must contain all fields and request data

prostospirit commented 3 months ago

I experimented a bit and realized that it is possible to override ModelAction.get_action

It will looks like a

   async def get_action(self, request: Request, **kwargs) -> Action:
        action = await super().get_action(request, **kwargs)
        node: AmisNode = getattr(action, action.actionType, None)
        if node:
            node.body = Service(
                schemaApi=AmisAPI(
                    method="post",
        >>>>>       url=self.router_path + self.page_path + "?item_id=${IF(promoaction_ids, promoaction_ids, promoaction_id)}",  # override default PK name
                    responseData={
                        "&": "${body}",
                        "api.url": "${body.api.url}?item_id=${api.query.item_id}",
                        "initApi.url": "${body.initApi.url}?item_id=${api.query.item_id}" if self.form_init else None,
                        "submitText": "",
                    },
                )
            )
        return action

and it will works correctly, but I think this should be provided by the functionality or there should be a more friendly interface for this

mmmcorpsvit commented 3 months ago

you can set

class CarCategoryAdmin(admin.ModelAdmin):
    page_schema = PageSchema(label="Car Category", icon="fa fa-truck")
    model = CarCategory

    pk_name: str = "id" # no ?
prostospirit commented 3 months ago

@mmmcorpsvit no, it doesn't work correctly for me to perform actions on records. If I set only pk_name, then item_id is empty when I make request for action from admin to backend.

the meaning is that I want to use the first key's name other than id

amisadmin commented 3 months ago

I'm not sure if this will work, but you can try the idea of defining a virtual id foreign key.

from sqlmodelx import SQLModel
from sqlmodel import Field
from sqlalchemy import column_property
from fastapi_amis_admin import admin

class Promoaction(SQLModel, table=True):
    __tablename__ = "promoaction"

    promoaction_id: int
    name: str

class Promoaction2(Promoaction, table=True):
    id: int = Field(
        None,
        title="Read-only primary key",
        sa_column=column_property(Promoaction.promoaction_id),
    )

class ActionAdmin(admin.ModelAdmin):
    page_schema = 'Promoaction Management'
    model = Promoaction2
prostospirit commented 2 months ago

@amisadmin

can try the idea of defining a virtual id foreign key.

I checked but it didn't work.

Traceback:

Traceback (most recent call last):
  File "/home/sergei/.pycharm_helpers/pydev/pydevd.py", line 1534, in _exec
    pydev_imports.execfile(file, globals, locals)  # execute the script
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/sergei/.pycharm_helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "..../admin_backend/main.py", line 19, in <module>
    site.mount_app(app)
  File "..../python3.12/site-packages/fastapi_amis_admin/admin/admin.py", line 1536, in mount_app
    self.register_router()
  File "..../python3.12/site-packages/fastapi_amis_admin/admin/admin.py", line 1402, in register_router
    self._register_admin_router_all()
  File "..../python3.12/site-packages/fastapi_amis_admin/admin/admin.py", line 1394, in _register_admin_router_all
    admin.register_router()
  File "..../python3.12/site-packages/fastapi_amis_admin/admin/admin.py", line 1400, in register_router
    self._create_admin_instance_all()
  File "..../python3.12/site-packages/fastapi_amis_admin/admin/admin.py", line 1386, in _create_admin_instance_all
    [self.get_admin_or_create(admin_cls) for admin_cls in self._registered.keys()]
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "..../python3.12/site-packages/fastapi_amis_admin/admin/admin.py", line 1361, in get_admin_or_create
    admin = admin_cls(self)
            ^^^^^^^^^^^^^^^
  File "..../python3.12/site-packages/fastapi_amis_admin/admin/admin.py", line 646, in __init__
    SqlalchemyCrud.__init__(self, self.model, self.engine)
  File "..../python3.12/site-packages/fastapi_amis_admin/crud/_sqlalchemy.py", line 265, in __init__
    SqlalchemySelector.__init__(self, model, fields)
  File "..../python3.12/site-packages/fastapi_amis_admin/crud/_sqlalchemy.py", line 124, in __init__
    assert self.fields, "fields is None"
AssertionError: fields is None