I have a working example here: https://github.com/citizen-stig/flask_admin_enum
I investigated it a little bit and maybe problem is in SQLalchemy and how it provides information about Enum column.
Since I use https://github.com/spoqa/sqlalchemy-enum34 I created custom enum fields.
Maybe it could work with the default enum column:
from enum import EnumMeta, Enum
from wtforms import SelectMultipleField
from flask_admin.form import Select2Field
from wtforms.compat import text_type
class EnumField:
@staticmethod
def get_name(enum_instance):
return enum_instance.name
@staticmethod
def get_value(enum_instance):
return enum_instance.value
default_label_function = get_value
def apply_choices(self, enum_class: EnumMeta, label_function=default_label_function, kwargs=None):
if not kwargs:
kwargs = dict()
choices = [(member.value, label_function(member)) for name, member in enum_class.__members__.items()]
kwargs['choices'] = choices
kwargs['coerce'] = self.coerce
return kwargs
@staticmethod
def coerce(data):
if isinstance(data, Enum):
return data.value
return text_type(data)
class SelectEnumField(Select2Field, EnumField):
def __init__(self, *args, enum_class: EnumMeta, label_function=EnumField.default_label_function, **kwargs):
kwargs = self.apply_choices(enum_class, label_function, kwargs)
super().__init__(*args, **kwargs)
class SelectMultipleEnumField(SelectMultipleField, EnumField):
def __init__(self, *args, enum_class: EnumMeta, label_function=EnumField.default_label_function, **kwargs):
kwargs = self.apply_choices(enum_class, label_function, kwargs)
super().__init__(*args, **kwargs)
You could of course edit the default_label_function to any function you want to create the label (there are two predefined ones - using the enum value: get_value and its name get_name. You could do better like getting the name and calling enum_instance.name.title() on it etc.
Is there a fix in place that hasn't been published yet or is a workaround the only short-term way forward. Is there an official workaround that is published and known to work?
We employed a similar workaround:
import sqlalchemy
from flask_admin.form.fields import Select2Field
from flask_admin.model.form import converts
class EnumField(Select2Field):
def __init__(self, column, **kwargs):
assert isinstance(column.type, sqlalchemy.sql.sqltypes.Enum)
def coercer(value):
# coerce incoming value into an enum value
if isinstance(value, column.type.enum_class):
return value
elif isinstance(value, basestring):
return column.type.enum_class[value]
else:
assert False
super(EnumField, self).__init__(
choices=[(v, v) for v in column.type.enums],
coerce=coercer,
**kwargs)
def pre_validate(self, form):
# we need to override the default SelectField validation because it
# apparently tries to directly compare the field value with the choice
# key; it is not clear how that could ever work in cases where the
# values and choice keys must be different types
for (v, _) in self.choices:
if self.data == self.coerce(v):
break
else:
raise ValueError(self.gettext('Not a valid choice'))
class CustomAdminConverter(flask_admin.contrib.sqla.form.AdminModelConverter):
@converts("sqlalchemy.sql.sqltypes.Enum")
def conv_enum(self, field_args, **extra):
return EnumField(column=extra["column"], **field_args)
class DefaultView(flask_admin.contrib.sqla.ModelView):
model_form_converter = CustomAdminConverter
No official fix pushed yet ?
FYI - Using this solution to declare the field does not conflict with the ENUM type https://stackoverflow.com/a/33042283/50348
And now I do not have to supply the form_override list either in Flask-Admin.
sale_status = db.Column(db.Enum('ACTIVE, 'SOLD, 'PENDING', name='sale_status_enum'))
From psql:
CREATE TYPE sale_status_enum AS ENUM (
'ACTIVE',
'SOLD',
'PENDING',
);
Most helpful comment
We employed a similar workaround: