piccolo-orm / piccolo_admin

A powerful web admin for your database.
https://piccolo-orm.com/ecosystem/
MIT License
299 stars 35 forks source link

Input field disappearing when clicking 'Add'. #386

Closed AmazingAkai closed 3 weeks ago

AmazingAkai commented 2 months ago
class NewStaff(BaseModel):
    name: Annotated[str, Field(max_length=255, pattern=r"^[a-zA-Z\s]+$")]
    email: EmailStr
    superuser: bool
    permissions: list[Permission]

async def new_staff_endpoint(request: Request, data: NewStaff) -> str:
    ... # user creation code...

NEW_STAFF_FORM = FormConfig(
    name="New Staff",
    description="Create a new staff member.",
    pydantic_model=NewStaff,
    endpoint=new_staff_endpoint,
)

Permission is just an enum

class Permission(Enum):
    UPLOADS = "uploads"
    NOTICES = "notices"
    GALLERY = "gallery"
    ADMISSIONS = "admissions"

Screencast_20240425_213515.webm

sinisaos commented 2 months ago

@AmazingAkai This can be fixed by including optional parameter on this line https://github.com/piccolo-orm/piccolo_admin/blob/1157c120415b54e509f7720daaa587cd2cc9d450/admin_ui/src/components/ArrayWidget.vue#L118-L120

like this

isMediaColumn() {
    return this.schema?.extra.media_columns.includes(this.fieldName)
}

Result is:

add_button.webm

But then we have another problem because Piccolo Admin doesn't understand "$ref": "#/$defs/Permission"(json reference) to Permission enum and enum attributes are not displayed as HTML select. It seems that custom forms can currently only work with str, int, bool etc. but not with json reference to enums or another Pydantic models.

dantownsend commented 2 months ago

@sinisaos I think adding this for now makes sense:

isMediaColumn() {
    return this.schema?.extra.media_columns.includes(this.fieldName)
}

At some point we need to refactor things a bit so InputField doesn't try getting the schema from the store, but I think this solution is good for now.

sinisaos commented 2 months ago

@dantownsend I agree with you and it will solve a smaller part of the problem. The larger part is that Piccolo Admin can't understand the json reference as I wrote in the previous comment and how to solve it so that custom forms work with enums and another Pydantic models as reference.

dantownsend commented 2 months ago

Yeah, OpenAPI schemas are surprisingly complex - especially the references etc.

sinisaos commented 2 months ago

@AmazingAkai I was thinking about how we could use a Python enum in array widget select in custom forms. I tried to mimic the Piccolo array column select and came up with this. These are the changes that should be made. We need to add this line of code to the NewForm.vue InputField like this

<InputField
    v-bind:isFilter="false"
    v-bind:key="columnName"
    v-bind:columnName="String(columnName)"
    v-bind:type="getType(property)"
    v-bind:value="property.default"
    v-bind:choices="property.extra?.choices" // new line
    v-bind:timeResolution="
        schema?.extra?.time_resolution[columnName]
    "
/>

After that, build the Vue frontend and add the dist folder to your local Piccolo Admin installation. The changes that need to be made to NewStaff model are as follows

# Permission enum is same as before
# with this we mimic Piccolo array column choices
choices = {}
for item in Permission:
    choices[item.name] = {
        "display_name": item.name,
        "value": item.value,
    }

class NewStaff(BaseModel):
    name: t.Annotated[str, Field(max_length=255, pattern=r"^[a-zA-Z\s]+$")]
    email: EmailStr
    superuser: bool
    permissions: t.List[t.Any] = Field(extra={"choices": choices}) # usage of choices

Result is this

enum_select.webm

This isn't perfect, but it might work in your case. I hope you find this useful.

AmazingAkai commented 2 months ago

Oh interesting, its better than nothing, thanks!

sinisaos commented 2 months ago

@dantownsend would it be good to add choices option to custom form InputField as in the previous comment? This would allow the user to write custom code to display enums in custom forms (either as a select or an array of select). Something like this

class Permission(str, enum.Enum):
    UPLOADS = "uploads"
    NOTICES = "notices"
    GALLERY = "gallery"
    ADMISSIONS = "admissions"

def custom_form_choices() -> t.Dict[str, t.Any]:
    choices = {}
    for item in Permission:
        choices[item.name] = {
            "display_name": item.name,
            "value": item.value,
        }
    return choices

class NewStaff(BaseModel):
    name: t.Annotated[str, Field(max_length=255, pattern=r"^[a-zA-Z\s]+$")]
    email: EmailStr
    superuser: bool
    permissions: t.Any = Field(  # or permissions: t.List[t.Any] if we want a list of selects as in the previous comment video
        json_schema_extra={"extra": {"choices": custom_form_choices()}}
    )

Result is this

custom_form_select.webm

What do you think about it?

AmazingAkai commented 2 months ago

@sinisaos I'm still having the same issue, I'm using piccolo_admin version 1.3.2, I tried installing the github version using pip install git+https://github.com/piccolo-orm/piccolo_admin but I was unable to start the project after installing the dev version.

dantownsend commented 2 months ago

I wonder if installing it from GitHub doesn't work because the Vue code isn't compiled into static assets.

I'll do a proper release later today.

dantownsend commented 2 months ago

Try piccolo_admin==1.3.3 - it's now on PyPI.

sinisaos commented 2 months ago

I wonder if installing it from GitHub doesn't work because the Vue code isn't compiled into static assets.

That exactly the case. If we want to use Piccolo Admin from Github, we need to compile the static files ourselves. With the new release, everything works as expected. @dantownsend What do you think of the choices from previous comments?

dantownsend commented 2 months ago

@sinisaos It's an interesting idea. I never considered this before:

permissions: t.Any = Field(  # or permissions: t.List[t.Any] if we want a list of selects as in the previous comment video
        json_schema_extra={"extra": {"choices": custom_form_choices()}}
    )

Is this just a documentation change, or requires other code changes?

sinisaos commented 2 months ago

@dantownsend Basically the only code change is adding a v-bind:choices="property.extra?.choices" to NewForm.vue InputField. The second change is the usage example in example.py. Here is the PR.