From c3e2432796d4c3139366325c693f4211f088b092 Mon Sep 17 00:00:00 2001 From: sinisaos Date: Sat, 25 Jan 2025 13:52:48 +0100 Subject: [PATCH 1/9] add enum support to custom forms --- admin_ui/src/components/NewForm.vue | 1 + e2e/test_forms.py | 26 ++++++++++++++++++ piccolo_admin/endpoints.py | 15 +++++++++++ piccolo_admin/example/forms/__init__.py | 2 ++ piccolo_admin/example/forms/enum.py | 35 +++++++++++++++++++++++++ piccolo_admin/utils.py | 13 +++++++++ tests/test_endpoints.py | 12 ++++++++- 7 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 piccolo_admin/example/forms/enum.py create mode 100644 piccolo_admin/utils.py diff --git a/admin_ui/src/components/NewForm.vue b/admin_ui/src/components/NewForm.vue index d31a7703..0128faae 100644 --- a/admin_ui/src/components/NewForm.vue +++ b/admin_ui/src/components/NewForm.vue @@ -12,6 +12,7 @@ v-bind:type="getType(property)" v-bind:value="property.default" v-bind:isNullable="isNullable(property)" + v-bind:choices="property.extra?.choices" v-bind:timeResolution=" schema?.extra?.time_resolution[columnName] " diff --git a/e2e/test_forms.py b/e2e/test_forms.py index 9af3cd92..9d265390 100644 --- a/e2e/test_forms.py +++ b/e2e/test_forms.py @@ -1,6 +1,7 @@ from playwright.sync_api import Page from piccolo_admin.example.forms.csv import FORM as CSV_FORM +from piccolo_admin.example.forms.enum import FORM as ENUM_FORM from piccolo_admin.example.forms.image import FORM as IMAGE_FORM from piccolo_admin.example.forms.nullable import FORM as NULLABLE_FORM @@ -79,3 +80,28 @@ def test_nullable_form(page: Page, dev_server): and response.status == 200 ): form_page.submit_form() + + +def test_form_enum(page: Page, dev_server): + """ + Make sure custom forms support the usage of Enum's. + """ + login_page = LoginPage(page=page) + login_page.reset() + login_page.login() + + form_page = FormPage( + page=page, + form_slug=ENUM_FORM.slug, + ) + form_page.reset() + page.locator('input[name="username"]').fill("piccolo") + page.locator('input[name="email"]').fill("piccolo@example.com") + page.locator('select[name="permissions"]').select_option("admissions") + + with page.expect_response( + lambda response: response.url == f"{BASE_URL}/api/forms/enum-form/" + and response.request.method == "POST" + and response.status == 200 + ): + form_page.submit_form() diff --git a/piccolo_admin/endpoints.py b/piccolo_admin/endpoints.py index 6405a070..5ba8f361 100644 --- a/piccolo_admin/endpoints.py +++ b/piccolo_admin/endpoints.py @@ -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 + pydantic_model.model_rebuild(force=True) class FormConfigResponseModel(BaseModel): diff --git a/piccolo_admin/example/forms/__init__.py b/piccolo_admin/example/forms/__init__.py index 6cf310ae..f009d574 100644 --- a/piccolo_admin/example/forms/__init__.py +++ b/piccolo_admin/example/forms/__init__.py @@ -1,6 +1,7 @@ from .calculator import FORM as CALCULATOR_FORM from .csv import FORM as CSV_FORM from .email import FORM as EMAIL_FORM +from .enum import FORM as ENUM_FORM from .image import FORM as IMAGE_FORM from .nullable import FORM as MEGA_FORM @@ -10,4 +11,5 @@ EMAIL_FORM, IMAGE_FORM, MEGA_FORM, + ENUM_FORM, ] diff --git a/piccolo_admin/example/forms/enum.py b/piccolo_admin/example/forms/enum.py new file mode 100644 index 00000000..1bb9f4a6 --- /dev/null +++ b/piccolo_admin/example/forms/enum.py @@ -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", +) diff --git a/piccolo_admin/utils.py b/piccolo_admin/utils.py new file mode 100644 index 00000000..2eaa1e85 --- /dev/null +++ b/piccolo_admin/utils.py @@ -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 diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 6eadb312..b76fdee8 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -311,6 +311,11 @@ def test_forms(self): "name": "Nullable fields", "slug": "nullable-fields", }, + { + "name": "Enum form", + "slug": "enum-form", + "description": "Make a enum form.", + }, ], ) @@ -526,7 +531,12 @@ def test_forms_grouped(self): "description": "Make a booking for a customer.", "name": "Booking form", "slug": "booking-form", - } + }, + { + "name": "Enum form", + "slug": "enum-form", + "description": "Make a enum form.", + }, ], "Test forms": [ { From 05dc4c8b86ec45af77e77c4914681e2d836ca5a9 Mon Sep 17 00:00:00 2001 From: sinisaos Date: Fri, 23 May 2025 10:06:51 +0200 Subject: [PATCH 2/9] applied @dantownsend suggestion --- docs/source/custom_forms/index.rst | 10 ++++++++++ piccolo_admin/endpoints.py | 13 +++++++++---- piccolo_admin/example/forms/enum.py | 5 ++++- piccolo_admin/translations/data.py | 6 +++--- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/docs/source/custom_forms/index.rst b/docs/source/custom_forms/index.rst index 60489e2e..33166a6c 100644 --- a/docs/source/custom_forms/index.rst +++ b/docs/source/custom_forms/index.rst @@ -39,6 +39,16 @@ Here's a more advanced example where we send an email, then return a string: .. literalinclude:: ../../../piccolo_admin/example/forms/email.py +``Enum`` +-------- + +Custom forms support ``Enum`` type. Here's a example: + +.. literalinclude:: ../../../piccolo_admin/example/forms/enum.py + +.. warning:: + We need to do the ``Enum`` type conversion from the form data ourselves. + ``FileResponse`` ---------------- diff --git a/piccolo_admin/endpoints.py b/piccolo_admin/endpoints.py index 5ba8f361..7b72110d 100644 --- a/piccolo_admin/endpoints.py +++ b/piccolo_admin/endpoints.py @@ -4,6 +4,7 @@ from __future__ import annotations +import enum import inspect import io import itertools @@ -396,7 +397,6 @@ 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 @@ -405,14 +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(): + for ( + field_name, + field_value, + ) in self.pydantic_model.model_fields.items(): + if isinstance(field_value.annotation, enum.EnumType): # 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) + "choices": convert_enum_to_choices( + field_value.annotation + ) } }, ) diff --git a/piccolo_admin/example/forms/enum.py b/piccolo_admin/example/forms/enum.py index 1bb9f4a6..52bb9ed8 100644 --- a/piccolo_admin/example/forms/enum.py +++ b/piccolo_admin/example/forms/enum.py @@ -18,9 +18,13 @@ class NewStaffModel(BaseModel): username: str email: EmailStr superuser: bool + permissions: Permission def new_staff_endpoint(request: Request, data: NewStaffModel) -> str: + # We need to do the enum type conversion ourselves like this: + # data.permissions = Permission(int(data.permissions)) # for int enum + # data.permissions = Permission(data.permissions) # for str enum print(data) return "A new staff member has been successfully created." @@ -30,6 +34,5 @@ def new_staff_endpoint(request: Request, data: NewStaffModel) -> str: pydantic_model=NewStaffModel, endpoint=new_staff_endpoint, description="Make a enum form.", - choices={"permissions": Permission}, form_group="Text forms", ) diff --git a/piccolo_admin/translations/data.py b/piccolo_admin/translations/data.py index 2f5ab9d5..23c8a295 100644 --- a/piccolo_admin/translations/data.py +++ b/piccolo_admin/translations/data.py @@ -1098,7 +1098,7 @@ FARSI = Translation( language_name="FARSI", language_code="fa", - translations = { + translations={ "About": "درباره", "Add Row": "افزودن ردیف", "Add": "افزودن", @@ -1115,7 +1115,7 @@ "Dark Mode": "حالت تاریک", "Days": "روز", "Delete": "حذف", - "Delimiter": "جداکننده", + "Delimiter": "جداکننده", "Descending": "نزولی", "Download": "دانلود", "Download failed": "دانلود ناموفق بود", @@ -1191,7 +1191,7 @@ TURKISH, UKRAINIAN, WELSH, - FARSI + FARSI, ] # We use some translations outside of the HTML templates (e.g. within From e62465ea7704f6358c7986f180a7590bc4f7dbc6 Mon Sep 17 00:00:00 2001 From: sinisaos Date: Fri, 23 May 2025 11:19:30 +0200 Subject: [PATCH 3/9] enable Enum type/meta in different Python versions --- piccolo_admin/endpoints.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/piccolo_admin/endpoints.py b/piccolo_admin/endpoints.py index 7b72110d..c4cb0447 100644 --- a/piccolo_admin/endpoints.py +++ b/piccolo_admin/endpoints.py @@ -11,6 +11,7 @@ import json import logging import os +import sys import typing as t from dataclasses import dataclass from datetime import timedelta @@ -66,6 +67,12 @@ from .utils import convert_enum_to_choices from .version import __VERSION__ as PICCOLO_ADMIN_VERSION +if sys.version_info < (3, 11): + ENUMTYPE = enum.EnumMeta +else: + ENUMTYPE = enum.EnumType + + logger = logging.getLogger(__name__) @@ -409,7 +416,7 @@ def __init__( field_name, field_value, ) in self.pydantic_model.model_fields.items(): - if isinstance(field_value.annotation, enum.EnumType): + if isinstance(field_value.annotation, ENUMTYPE): # update model_fields, field annotation and # rebuild the model for the changes to take effect pydantic_model.model_fields[field_name] = Field( From b08950e50d6f9904a0893cdbba8a0d37586b46ac Mon Sep 17 00:00:00 2001 From: sinisaos Date: Fri, 30 May 2025 21:51:47 +0200 Subject: [PATCH 4/9] applied @dantownsend suggestion --- piccolo_admin/endpoints.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/piccolo_admin/endpoints.py b/piccolo_admin/endpoints.py index c4cb0447..2c14f51d 100644 --- a/piccolo_admin/endpoints.py +++ b/piccolo_admin/endpoints.py @@ -11,7 +11,6 @@ import json import logging import os -import sys import typing as t from dataclasses import dataclass from datetime import timedelta @@ -67,12 +66,6 @@ from .utils import convert_enum_to_choices from .version import __VERSION__ as PICCOLO_ADMIN_VERSION -if sys.version_info < (3, 11): - ENUMTYPE = enum.EnumMeta -else: - ENUMTYPE = enum.EnumType - - logger = logging.getLogger(__name__) @@ -416,8 +409,10 @@ def __init__( field_name, field_value, ) in self.pydantic_model.model_fields.items(): - if isinstance(field_value.annotation, ENUMTYPE): - # update model_fields, field annotation and + if inspect.isclass(field_value.annotation) and issubclass( + field_value.annotation, enum.Enum + ): + # 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={ From decc3158d0696b7fa368bc146414c7a94f6f8fa0 Mon Sep 17 00:00:00 2001 From: sinisaos Date: Mon, 9 Jun 2025 08:30:13 +0200 Subject: [PATCH 5/9] use built-in types --- piccolo_admin/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/piccolo_admin/utils.py b/piccolo_admin/utils.py index 2eaa1e85..b972cc62 100644 --- a/piccolo_admin/utils.py +++ b/piccolo_admin/utils.py @@ -1,9 +1,9 @@ from __future__ import annotations -import typing as t +from typing import Any -def convert_enum_to_choices(enum_data: t.Any) -> t.Dict[str, t.Any]: +def convert_enum_to_choices(enum_data: Any) -> dict[str, Any]: choices = {} for item in enum_data: choices[item.name] = { From 7c1198f26f909283506172ed08464315132077e6 Mon Sep 17 00:00:00 2001 From: sinisaos Date: Mon, 9 Jun 2025 14:15:56 +0200 Subject: [PATCH 6/9] Update docs/source/custom_forms/index.rst Co-authored-by: Ethan <47520067+Skelmis@users.noreply.github.com> --- docs/source/custom_forms/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/custom_forms/index.rst b/docs/source/custom_forms/index.rst index 33166a6c..09e67f1c 100644 --- a/docs/source/custom_forms/index.rst +++ b/docs/source/custom_forms/index.rst @@ -47,7 +47,7 @@ Custom forms support ``Enum`` type. Here's a example: .. literalinclude:: ../../../piccolo_admin/example/forms/enum.py .. warning:: - We need to do the ``Enum`` type conversion from the form data ourselves. + We need to do the ``Enum`` type conversion from the form data ourselves as a result of how this feature is implemented. If you don't do this conversion, then the field with be provided as a ``str`` instead of the ``Enum``. ``FileResponse`` ---------------- From a51ac5a3a04f5874925d6882851ab88c2dacd5b4 Mon Sep 17 00:00:00 2001 From: Egor Zindy Date: Mon, 9 Feb 2026 21:32:54 +0100 Subject: [PATCH 7/9] Optional flag and default value for form enums --- admin_ui/src/components/ChoiceSelect.vue | 8 +++- piccolo_admin/endpoints.py | 60 +++++++++++++++++++----- piccolo_admin/example/forms/enum.py | 9 +++- piccolo_admin/utils.py | 2 +- 4 files changed, 64 insertions(+), 15 deletions(-) diff --git a/admin_ui/src/components/ChoiceSelect.vue b/admin_ui/src/components/ChoiceSelect.vue index 39fb28be..678727cb 100644 --- a/admin_ui/src/components/ChoiceSelect.vue +++ b/admin_ui/src/components/ChoiceSelect.vue @@ -56,12 +56,16 @@ export default defineComponent({ }, data() { return { - localValue: "" as string | undefined + localValue: undefined as string | number | undefined } }, emits: ["updated"], mounted() { - this.localValue = this.isFilter ? "all" : this.value + if (this.isFilter) { + this.localValue = "all" + } else if (this.value !== undefined) { + this.localValue = this.value + } }, watch: { value(newValue) { diff --git a/piccolo_admin/endpoints.py b/piccolo_admin/endpoints.py index fdeeebb8..d4b8a593 100644 --- a/piccolo_admin/endpoints.py +++ b/piccolo_admin/endpoints.py @@ -51,6 +51,7 @@ from piccolo_api.session_auth.middleware import SessionsAuthBackend from piccolo_api.session_auth.tables import SessionsBase from pydantic import BaseModel, Field, ValidationError +from pydantic_core import PydanticUndefined from starlette.middleware import Middleware from starlette.middleware.authentication import AuthenticationMiddleware from starlette.middleware.exceptions import HTTPException @@ -410,21 +411,58 @@ def __init__( field_name, field_value, ) in self.pydantic_model.model_fields.items(): - if inspect.isclass(field_value.annotation) and issubclass( - field_value.annotation, enum.Enum + # Extract the actual type, handling Optional/Union + field_type = field_value.annotation + is_optional = False + + # Check if it's Optional (Union with None) + if hasattr(field_type, '__origin__') and field_type.__origin__ is Union: + # Get all args from Union, filter out NoneType + type_args = [arg for arg in field_type.__args__ if arg is not type(None)] + if len(type_args) == 1: + field_type = type_args[0] + is_optional = True + + if inspect.isclass(field_type) and issubclass( + field_type, enum.Enum ): - # update model fields, field annotation and + # Get the default value if it exists + default_value = None + if field_value.default is not None and field_value.default is not PydanticUndefined: + # If default is an Enum instance, get its value + if isinstance(field_value.default, enum.Enum): + default_value = field_value.default.value + else: + default_value = field_value.default + + # Update model fields, field annotation and # rebuild the model for the changes to take effect + json_schema_extra_dict = { + "extra": { + "choices": convert_enum_to_choices(field_type) + } + } + + # Add default value if it exists + if default_value is not None: + json_schema_extra_dict["extra"]["default"] = default_value + pydantic_model.model_fields[field_name] = Field( - json_schema_extra={ - "extra": { - "choices": convert_enum_to_choices( - field_value.annotation - ) - } - }, + default=default_value, + description=field_value.description, + title=field_value.title, + json_schema_extra=json_schema_extra_dict, ) - pydantic_model.model_fields[field_name].annotation = str + + # Detect the enum value type (int, str, etc.) + enum_value_type = type(next(iter(field_type)).value) + + # Set annotation to Optional[type] or type depending on original + if is_optional: + pydantic_model.model_fields[field_name].annotation = Optional[enum_value_type] + else: + pydantic_model.model_fields[field_name].annotation = enum_value_type + pydantic_model.model_rebuild(force=True) diff --git a/piccolo_admin/example/forms/enum.py b/piccolo_admin/example/forms/enum.py index 52bb9ed8..251c933d 100644 --- a/piccolo_admin/example/forms/enum.py +++ b/piccolo_admin/example/forms/enum.py @@ -1,4 +1,5 @@ import enum +from typing import Optional from pydantic import BaseModel, EmailStr from starlette.requests import Request @@ -13,12 +14,18 @@ class Permission(str, enum.Enum): notices = "notices" uploads = "uploads" +class LogLevel(int, enum.Enum): + error = 2 + warn = 3 + info = 4 + debug = 5 class NewStaffModel(BaseModel): username: str email: EmailStr superuser: bool - permissions: Permission + permissions: Permission="gallery" + log_level: Optional[LogLevel] def new_staff_endpoint(request: Request, data: NewStaffModel) -> str: diff --git a/piccolo_admin/utils.py b/piccolo_admin/utils.py index b972cc62..4e653dcc 100644 --- a/piccolo_admin/utils.py +++ b/piccolo_admin/utils.py @@ -7,7 +7,7 @@ def convert_enum_to_choices(enum_data: Any) -> dict[str, Any]: choices = {} for item in enum_data: choices[item.name] = { - "display_name": item.name, + "display_name": item.name.replace('_', ' '), "value": item.value, } return choices From 548a0d9d6f6b8250a64217569e0ea3c2be5acfbf Mon Sep 17 00:00:00 2001 From: sinisaos Date: Tue, 24 Feb 2026 21:31:53 +0100 Subject: [PATCH 8/9] fix some linter errors from @zindy pr --- piccolo_admin/endpoints.py | 54 ++++++++++++++++++----------- piccolo_admin/example/forms/enum.py | 7 ++-- piccolo_admin/translations/data.py | 28 ++++++--------- piccolo_admin/utils.py | 2 +- 4 files changed, 47 insertions(+), 44 deletions(-) diff --git a/piccolo_admin/endpoints.py b/piccolo_admin/endpoints.py index d4b8a593..0ea21ef1 100644 --- a/piccolo_admin/endpoints.py +++ b/piccolo_admin/endpoints.py @@ -15,7 +15,7 @@ from dataclasses import dataclass from datetime import timedelta from functools import partial -from typing import Any, Optional, TypeVar, Union +from typing import Any, Optional, TypeVar, Union, cast import typing_extensions from fastapi import FastAPI, File, Form, UploadFile @@ -414,21 +414,30 @@ def __init__( # Extract the actual type, handling Optional/Union field_type = field_value.annotation is_optional = False - + assert field_type + # Check if it's Optional (Union with None) - if hasattr(field_type, '__origin__') and field_type.__origin__ is Union: + if ( + hasattr(field_type, "__origin__") + and field_type.__origin__ is Union + ): # Get all args from Union, filter out NoneType - type_args = [arg for arg in field_type.__args__ if arg is not type(None)] + type_args = [ + arg for arg in field_type.__args__ if arg is not type(None) + ] if len(type_args) == 1: field_type = type_args[0] is_optional = True - + if inspect.isclass(field_type) and issubclass( field_type, enum.Enum ): # Get the default value if it exists default_value = None - if field_value.default is not None and field_value.default is not PydanticUndefined: + if ( + field_value.default is not None + and field_value.default is not PydanticUndefined + ): # If default is an Enum instance, get its value if isinstance(field_value.default, enum.Enum): default_value = field_value.default.value @@ -437,32 +446,35 @@ def __init__( # Update model fields, field annotation and # rebuild the model for the changes to take effect - json_schema_extra_dict = { - "extra": { - "choices": convert_enum_to_choices(field_type) - } + json_schema_extra_dict: dict[str, Any] = { + "extra": {"choices": convert_enum_to_choices(field_type)} } - + # Add default value if it exists if default_value is not None: json_schema_extra_dict["extra"]["default"] = default_value - pydantic_model.model_fields[field_name] = Field( + pydantic_model.model_fields[field_name] = Field( # type:ignore default=default_value, description=field_value.description, title=field_value.title, json_schema_extra=json_schema_extra_dict, ) - + # Detect the enum value type (int, str, etc.) - enum_value_type = type(next(iter(field_type)).value) - - # Set annotation to Optional[type] or type depending on original - if is_optional: - pydantic_model.model_fields[field_name].annotation = Optional[enum_value_type] - else: - pydantic_model.model_fields[field_name].annotation = enum_value_type - + enum_member = next(iter(field_type)) + enum_value_type = type(enum_member.value) + + # Set annotation to Optional[type] or type depending + # on original + new_annotation = ( + enum_value_type | None if is_optional else enum_value_type + ) + + pydantic_model.model_fields[field_name].annotation = cast( + Any, new_annotation + ) + pydantic_model.model_rebuild(force=True) diff --git a/piccolo_admin/example/forms/enum.py b/piccolo_admin/example/forms/enum.py index 251c933d..adf3e4fb 100644 --- a/piccolo_admin/example/forms/enum.py +++ b/piccolo_admin/example/forms/enum.py @@ -14,24 +14,23 @@ class Permission(str, enum.Enum): notices = "notices" uploads = "uploads" + class LogLevel(int, enum.Enum): error = 2 warn = 3 info = 4 debug = 5 + class NewStaffModel(BaseModel): username: str email: EmailStr superuser: bool - permissions: Permission="gallery" + permissions: Permission = Permission.gallery log_level: Optional[LogLevel] def new_staff_endpoint(request: Request, data: NewStaffModel) -> str: - # We need to do the enum type conversion ourselves like this: - # data.permissions = Permission(int(data.permissions)) # for int enum - # data.permissions = Permission(data.permissions) # for str enum print(data) return "A new staff member has been successfully created." diff --git a/piccolo_admin/translations/data.py b/piccolo_admin/translations/data.py index 0730214c..2e1322a6 100644 --- a/piccolo_admin/translations/data.py +++ b/piccolo_admin/translations/data.py @@ -1279,8 +1279,7 @@ "Edit": "Szerkesztés", "Export CSV": "CSV exportálása", "Filter": "Szűrés", - "For timestamps which are timezone aware, they will be displayed in this timezone by default.": - "Az időzóna-érzékeny időbélyegek alapértelmezés szerint ebben az időzónában jelennek meg.", + "For timestamps which are timezone aware, they will be displayed in this timezone by default.": "Az időzóna-érzékeny időbélyegek alapértelmezés szerint ebben az időzónában jelennek meg.", "Form submitted": "Űrlap elküldve", "Forms": "Űrlapok", "Go to page": "Ugrás az oldalra", @@ -1300,10 +1299,8 @@ "New password": "Új jelszó", "New value": "Új érték", "No results found": "Nincs találat", - "Note: Large data sets may take a while.": - "Megjegyzés: Nagy adathalmazok feldolgozása eltarthat egy ideig.", - "Note: They are converted to UTC when stored in the database.": - "Megjegyzés: Az adatbázisban UTC időzónára vannak konvertálva.", + "Note: Large data sets may take a while.": "Megjegyzés: Nagy adathalmazok feldolgozása eltarthat egy ideig.", + "Note: They are converted to UTC when stored in the database.": "Megjegyzés: Az adatbázisban UTC időzónára vannak konvertálva.", "of": "/", "page": "oldal", "Password": "Jelszó", @@ -1313,8 +1310,7 @@ "Seconds": "Másodpercek", "Select a column to update": "Válasszon egy frissítendő oszlopot", "Select a Column": "Oszlop kiválasztása", - "Select a table in the sidebar to get started.": - "A kezdéshez válasszon egy táblát az oldalsávban.", + "Select a table in the sidebar to get started.": "A kezdéshez válasszon egy táblát az oldalsávban.", "selected result(s) on": "kiválasztott találat itt:", "Semicolon": "Pontosvessző", "Set Timezone": "Időzóna beállítása", @@ -1334,7 +1330,7 @@ "Weeks": "Hetek", "Welcome to": "Üdvözöljük itt:", "with a matching": "egyező értékkel", - } + }, ) SLOVAK = Translation( @@ -1365,8 +1361,7 @@ "Edit": "Upraviť", "Export CSV": "Exportovať CSV", "Filter": "Filter", - "For timestamps which are timezone aware, they will be displayed in this timezone by default.": - "Časové značky s informáciou o časovom pásme sa predvolene zobrazujú v tomto časovom pásme.", + "For timestamps which are timezone aware, they will be displayed in this timezone by default.": "Časové značky s informáciou o časovom pásme sa predvolene zobrazujú v tomto časovom pásme.", "Form submitted": "Formulár odoslaný", "Forms": "Formuláre", "Go to page": "Prejsť na stránku", @@ -1386,10 +1381,8 @@ "New password": "Nové heslo", "New value": "Nová hodnota", "No results found": "Neboli nájdené žiadne výsledky", - "Note: Large data sets may take a while.": - "Poznámka: Spracovanie veľkých dátových súborov môže chvíľu trvať.", - "Note: They are converted to UTC when stored in the database.": - "Poznámka: Pri ukladaní do databázy sa konvertujú na UTC.", + "Note: Large data sets may take a while.": "Poznámka: Spracovanie veľkých dátových súborov môže chvíľu trvať.", + "Note: They are converted to UTC when stored in the database.": "Poznámka: Pri ukladaní do databázy sa konvertujú na UTC.", "of": "z", "page": "strana", "Password": "Heslo", @@ -1399,8 +1392,7 @@ "Seconds": "Sekundy", "Select a column to update": "Vyberte stĺpec na aktualizáciu", "Select a Column": "Vybrať stĺpec", - "Select a table in the sidebar to get started.": - "Ak chcete začať, vyberte tabuľku na bočnom paneli.", + "Select a table in the sidebar to get started.": "Ak chcete začať, vyberte tabuľku na bočnom paneli.", "selected result(s) on": "vybrané výsledky na", "Semicolon": "Bodkočiarka", "Set Timezone": "Nastaviť časové pásmo", @@ -1420,7 +1412,7 @@ "Weeks": "Týždne", "Welcome to": "Vitajte v", "with a matching": "so zhodou", - } + }, ) TRANSLATIONS: list[Translation] = [ diff --git a/piccolo_admin/utils.py b/piccolo_admin/utils.py index 4e653dcc..5a1fdd08 100644 --- a/piccolo_admin/utils.py +++ b/piccolo_admin/utils.py @@ -7,7 +7,7 @@ def convert_enum_to_choices(enum_data: Any) -> dict[str, Any]: choices = {} for item in enum_data: choices[item.name] = { - "display_name": item.name.replace('_', ' '), + "display_name": item.name.replace("_", " "), "value": item.value, } return choices From 5e03a848cf5327a6781f841dd93d57cbfdb9bf60 Mon Sep 17 00:00:00 2001 From: sinisaos Date: Thu, 26 Feb 2026 11:54:49 +0100 Subject: [PATCH 9/9] removed type conversion warning from docs --- docs/source/custom_forms/index.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/source/custom_forms/index.rst b/docs/source/custom_forms/index.rst index 09e67f1c..1152750c 100644 --- a/docs/source/custom_forms/index.rst +++ b/docs/source/custom_forms/index.rst @@ -46,9 +46,6 @@ Custom forms support ``Enum`` type. Here's a example: .. literalinclude:: ../../../piccolo_admin/example/forms/enum.py -.. warning:: - We need to do the ``Enum`` type conversion from the form data ourselves as a result of how this feature is implemented. If you don't do this conversion, then the field with be provided as a ``str`` instead of the ``Enum``. - ``FileResponse`` ----------------