问题
My Python Flask app is using WTForms with built in python Enum support. I'm attempting to submit a form (POST) where a SelectField is populated by all values of a Enum.
When I hit 'Submit' i'm given the error, 'Not a valid choice.' This seems strange because when checking the values of the incoming form, the form seemingly does contain a valid choice from the list of Enum values provided.
I'm using a subclass of Enum named AJBEnum
which is formatted like so:
class UserRole(AJBEnum):
admin = 0
recipient = 1
I chose to do this because I use many Enums through the project and wanted to write a helper function that gathers all choices and formats them WTForm SelectField tuple friendly. AJBEnum is formatted like so:
class AJBEnum(Enum):
@classmethod
def choices(cls, blank=True):
choices = []
if blank == True:
choices += [("", "")]
choices += [(choice, choice.desc()) for choice in cls]
return choices
Which means I can give WTForms all choices for UserRole
during the creating of the SelectField like so:
role = SelectField('Role', choices=UserRole.choices(blank=False), default=UserRole.recipient)
Note the function parameter blank
provides an additional blank SelectField option in case the SelectField is optional. In this case, it is not.
When I hit the Submit button I check the incoming incoming request in my routes and by printing the form.data
i'm given this content:
{'email': 'abc@gmail.com', 'password': 'fake', 'plan': 'A', 'confirm': 'fake', 'submit': True, 'id': None, 'role': 'UserRole.recipient'}
As you can see, it appears WTForms has stringified UserRole.recipient. Is there a way to coerce WTForms into converting the incoming POST request value back to the Enum value that it was intended to be?
回答1:
Is there a way to coerce WTForms
The argument you're looking for is actually called coerce
, and it accepts a callable that converts the string representation of the field to the choice's value.
- The choice value should be an
Enum
instance - The field value should be
str(Enum.value)
- The field text should be
Enum.name
To accomplish this, I've extended Enum
with some WTForms
helpers:
class FormEnum(Enum):
@classmethod
def choices(cls):
return [(choice, choice.name) for choice in cls]
@classmethod
def coerce(cls, item):
return cls(int(item)) if not isinstance(item, cls) else item
def __str__(self):
return str(self.value)
You can then edit a FormEnum
derived value using a SelectField
:
role = SelectField(
"Role",
choices = UserRole.choices(),
coerce = UserRole.coerce)
来源:https://stackoverflow.com/questions/43160780/python-flask-wtform-selectfield-with-enum-values-not-a-valid-choice-upon-valid