-
Notifications
You must be signed in to change notification settings - Fork 45
add enum support to custom forms #436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
c3e2432
a0711c4
90e92b5
05dc4c8
e62465e
4a14402
b08950e
eb0f903
decc315
7c1198f
8703d24
3cb1f9a
549d1d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,6 +62,7 @@ | |
| TranslationListItem, | ||
| TranslationListResponse, | ||
| ) | ||
| from .utils import convert_enum_to_choices | ||
| from .version import __VERSION__ as PICCOLO_ADMIN_VERSION | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
@@ -395,6 +396,7 @@ def __init__( | |
| t.Union[FormResponse, t.Coroutine[None, None, FormResponse]], | ||
| ], | ||
| description: t.Optional[str] = None, | ||
| choices: t.Optional[t.Dict[str, t.Any]] = None, | ||
| form_group: t.Optional[str] = None, | ||
| ): | ||
| self.name = name | ||
|
|
@@ -403,6 +405,19 @@ def __init__( | |
| self.description = description | ||
| self.form_group = form_group | ||
| self.slug = self.name.replace(" ", "-").lower() | ||
| if choices is not None: | ||
| for field_name, field_value in choices.items(): | ||
| # update model_fields, field annotation and | ||
| # rebuild the model for the changes to take effect | ||
| pydantic_model.model_fields[field_name] = Field( | ||
| json_schema_extra={ | ||
| "extra": { | ||
| "choices": convert_enum_to_choices(field_value) | ||
| } | ||
| }, | ||
| ) | ||
| pydantic_model.model_fields[field_name].annotation = str | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't we just leave the annotation as what it was originally?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to reassign the annotation type because if we don't, that annotation is
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, yes - I forgot that the front end would get confused by the open api schema is we leave the enum in there. |
||
| pydantic_model.model_rebuild(force=True) | ||
|
|
||
|
|
||
| class FormConfigResponseModel(BaseModel): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import enum | ||
|
|
||
| from pydantic import BaseModel, EmailStr | ||
| from starlette.requests import Request | ||
|
|
||
| from piccolo_admin.endpoints import FormConfig | ||
|
|
||
|
|
||
| # An example of using Python enum in custom forms | ||
| class Permission(str, enum.Enum): | ||
| admissions = "admissions" | ||
| gallery = "gallery" | ||
| notices = "notices" | ||
| uploads = "uploads" | ||
|
|
||
|
|
||
| class NewStaffModel(BaseModel): | ||
| username: str | ||
| email: EmailStr | ||
| superuser: bool | ||
|
|
||
|
|
||
| def new_staff_endpoint(request: Request, data: NewStaffModel) -> str: | ||
| print(data) | ||
| return "A new staff member has been successfully created." | ||
|
|
||
|
|
||
| FORM = FormConfig( | ||
| name="Enum form", | ||
| pydantic_model=NewStaffModel, | ||
| endpoint=new_staff_endpoint, | ||
| description="Make a enum form.", | ||
| choices={"permissions": Permission}, | ||
| form_group="Text forms", | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import typing as t | ||
|
|
||
|
|
||
| def convert_enum_to_choices(enum_data: t.Any) -> t.Dict[str, t.Any]: | ||
| choices = {} | ||
| for item in enum_data: | ||
| choices[item.name] = { | ||
| "display_name": item.name, | ||
| "value": item.value, | ||
| } | ||
| return choices |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than passing the choices in, can't we just look at the enum value on the pydantic model itself (i.e. if the default value for the field is an enum then use it to generate the choices).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think something like this?
Code changes
Usage would be
Usage example