Repository: flask-admin/flask-admin

Issue:
Flask-Admin doesn't populates correct value from Enum column
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.



Discussion:
Since I use https://github.com/spoqa/sqlalchemy-enum34 I created custom enum fields.
Maybe it could work with the default enum column:

``` python
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:

``` python
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 ?

Resolving patch:
diff --git a/flask_admin/contrib/sqla/form.py b/flask_admin/contrib/sqla/form.py
--- a/flask_admin/contrib/sqla/form.py
+++ b/flask_admin/contrib/sqla/form.py
@@ -1,4 +1,5 @@
 import warnings
+from enum import Enum
 
 from wtforms import fields, validators
 from sqlalchemy import Boolean, Column
@@ -9,7 +10,7 @@
 from flask_admin.model.fields import AjaxSelectField, AjaxSelectMultipleField
 from flask_admin.model.helpers import prettify_name
 from flask_admin._backwards import get_property
-from flask_admin._compat import iteritems
+from flask_admin._compat import iteritems, text_type
 
 from .validators import Unique
 from .fields import (QuerySelectField, QuerySelectMultipleField,
@@ -281,6 +282,7 @@ def conv_String(self, column, field_args, **extra):
                 accepted_values.append(None)
 
             field_args['validators'].append(validators.AnyOf(accepted_values))
+            field_args['coerce'] = lambda v: v.name if isinstance(v, Enum) else text_type(v)
 
             return form.Select2Field(**field_args)
 


Test patch: