kvesteri / wtforms-alchemy

Tools for creating wtforms from sqlalchemy models
Other
245 stars 59 forks source link

Handle Enum columns backed by Python Enums #164

Open Vidminas opened 1 year ago

Vidminas commented 1 year ago

In https://github.com/kvesteri/wtforms-alchemy/commit/bfc25f896725b41a2ca2f12c64501f72fad21a4b, support for Enums backing ChoiceType was added. But this doesn't cover native Enums directly backing the SQLAlchemy Enum column type (supported since SQLAlchemy v1.1).

For example, with an enum and an SQLAlchemy class that contains it like this:

class MaterialType(Enum):
    POWDER = "Powder"
    SAND = "Sand"
    GRAVEL = "Gravel"
    BOULDER = "Boulder"

class Material(Base):
    __tablename__ = "material"
    material_type: Mapped[MaterialType]

My app crashes with a ValueError: 'POWDER' is not a valid MaterialType when attempting to render the form, as the default selected option contains an invalid value.

I found several similar (but not the same) issues reported elsewhere:

I've added an additional case to handle this in WTForms-Alchemy > generator.py > select_field_kwargs.

Before the change:

================================================= test session starts =================================================
platform win32 -- Python 3.11.4, pytest-7.4.0, pluggy-1.2.0
cachedir: .tox\sqlalchemy14\.pytest_cache
rootdir: C:\Users\Vidminas\GitHub\wtforms-alchemy
plugins: anyio-3.7.1, cov-4.1.0
collected 249 items

tests\test_class_map.py .................                                                                        [  6%]
tests\test_column_aliases.py .....                                                                               [  8%]
tests\test_configuration.py ...........F......                                                                   [ 16%]
tests\test_country_field.py ..                                                                                   [ 16%]
tests\test_custom_fields.py .                                                                                    [ 17%]
tests\test_deep_form_relations.py ..                                                                             [ 18%]
tests\test_descriptions.py ..                                                                                    [ 18%]
tests\test_field_exclusion.py ....                                                                               [ 20%]
tests\test_field_order.py .                                                                                      [ 20%]
tests\test_field_parameters.py .............                                                                     [ 26%]
tests\test_field_trimming.py ..                                                                                  [ 26%]
tests\test_form_meta.py ........                                                                                 [ 30%]
tests\test_hybrid_properties.py ..                                                                               [ 30%]
tests\test_i18n_extension.py ...                                                                                 [ 32%]
tests\test_inheritance.py .....                                                                                  [ 34%]
tests\test_labels.py ..                                                                                          [ 34%]
tests\test_model_field_list.py ........                                                                          [ 38%]
tests\test_model_form_factory.py ............                                                                    [ 42%]
tests\test_model_form_field.py ...                                                                               [ 44%]
tests\test_phone_number.py ....                                                                                  [ 45%]
tests\test_phone_number_field.py ............                                                                    [ 50%]
tests\test_query_select_field.py .............                                                                   [ 55%]
tests\test_select_field.py ..........                                                                            [ 59%]
tests\test_synonym.py ..                                                                                         [ 60%]
tests\test_types.py ................F..............................                                              [ 79%]
tests\test_unique_validator.py .......................                                                           [ 88%]
tests\test_utils.py .                                                                                            [ 89%]
tests\test_validators.py ...................                                                                     [ 96%]
tests\test_weekdays_field.py ..                                                                                  [ 97%]
tests\test_widgets.py ......                                                                                     [100%]

====================================================== FAILURES =======================================================
___________________________ TestModelFormConfiguration.test_supports_custom_datetime_format ___________________________

self = <tests.test_configuration.TestModelFormConfiguration object at 0x00000186FC071350>

    def test_supports_custom_datetime_format(self):
        self.init(sa.DateTime, nullable=False)

        class ModelTestForm(ModelForm):
            class Meta:
                model = self.ModelTest
                datetime_format = '%Y-%m-%dT%H:%M:%S'

        form = ModelTestForm()
>       assert form.test_column.format == '%Y-%m-%dT%H:%M:%S'
E       AssertionError: assert ['%Y-%m-%dT%H:%M:%S'] == '%Y-%m-%dT%H:%M:%S'
E        +  where ['%Y-%m-%dT%H:%M:%S'] = <wtforms_components.fields.html5.DateTimeField object at 0x00000186FC4C0210>.format
E        +    where <wtforms_components.fields.html5.DateTimeField object at 0x00000186FC4C0210> = <tests.test_configuration.TestModelFormConfiguration.test_supports_custom_datetime_format.<locals>.ModelTestForm object at 0x00000186FC4C2350>.test_column

tests\test_configuration.py:154: AssertionError
______________ TestModelColumnToFormFieldTypeConversion.test_builtin_enum_field_converts_to_select_field ______________

self = <tests.test_types.TestModelColumnToFormFieldTypeConversion object at 0x00000186FC479F50>

    def test_builtin_enum_field_converts_to_select_field(self):
        class TestEnum(Enum):
            A = 'a'
            B = 'b'
        self.init(type_=sa.Enum(TestEnum))
        self.assert_type('test_column', SelectField)
        form = self.form_class()
>       assert form.test_column.choices == [('a', 'A'), ('b', 'B')]
E       AssertionError: assert [('A', 'A'), ('B', 'B')] == [('a', 'A'), ('b', 'B')]
E         At index 0 diff: ('A', 'A') != ('a', 'A')
E         Use -v to get more diff

tests\test_types.py:150: AssertionError
================================================== warnings summary ===================================================
tests\test_unique_validator.py:12
  C:\Users\Vidminas\GitHub\wtforms-alchemy\tests\test_unique_validator.py:12: MovedIn20Warning: Deprecated API features detected! These feature(s) are not compatible with SQLAlchemy 2.0. To prevent incompatible upgrades prior to updating applications, ensure requirements files are pinned to "sqlalchemy<2.0". Set environment variable SQLALCHEMY_WARN_20=1 to show all deprecation warnings.  Set environment variable SQLALCHEMY_SILENCE_UBER_WARNING=1 to silence this message. (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)
    base = declarative_base()

tests/test_model_form_factory.py::TestModelFormFactory::test_class_meta_wtforms2
tests/test_model_form_factory.py::TestModelFormFactory::test_class_meta_wtforms2
  C:\Users\Vidminas\GitHub\wtforms-alchemy\tests\test_model_form_factory.py:94: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    if LooseVersion(wtforms.__version__) < LooseVersion('2'):

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=============================================== short test summary info ===============================================
FAILED tests/test_configuration.py::TestModelFormConfiguration::test_supports_custom_datetime_format - AssertionError...
FAILED tests/test_types.py::TestModelColumnToFormFieldTypeConversion::test_builtin_enum_field_converts_to_select_field
====================================== 2 failed, 247 passed, 3 warnings in 4.60s ======================================
sqlalchemy14: exit 1 (9.26 seconds) C:\Users\Vidminas\GitHub\wtforms-alchemy> py.test pid=21592
.pkg: _exit> python C:\Users\Vidminas\.conda\envs\wtforms-alchemy\Lib\site-packages\pyproject_api\_backend.py True setuptools.build_meta __legacy__
  sqlalchemy14: FAIL code 1 (218.98=setup[124.92]+cmd[84.80,9.26] seconds)
  evaluation failed :( (219.48 seconds)

After the change:

================================================= test session starts =================================================
platform win32 -- Python 3.11.4, pytest-7.4.0, pluggy-1.2.0
cachedir: .tox\sqlalchemy14\.pytest_cache
rootdir: C:\Users\Vidminas\GitHub\wtforms-alchemy
plugins: anyio-3.7.1, cov-4.1.0
collected 249 items

tests\test_class_map.py .................                                                                        [  6%]
tests\test_column_aliases.py .....                                                                               [  8%]
tests\test_configuration.py ...........F......                                                                   [ 16%]
tests\test_country_field.py ..                                                                                   [ 16%]
tests\test_custom_fields.py .                                                                                    [ 17%]
tests\test_deep_form_relations.py ..                                                                             [ 18%]
tests\test_descriptions.py ..                                                                                    [ 18%]
tests\test_field_exclusion.py ....                                                                               [ 20%]
tests\test_field_order.py .                                                                                      [ 20%]
tests\test_field_parameters.py .............                                                                     [ 26%]
tests\test_field_trimming.py ..                                                                                  [ 26%]
tests\test_form_meta.py ........                                                                                 [ 30%]
tests\test_hybrid_properties.py ..                                                                               [ 30%]
tests\test_i18n_extension.py ...                                                                                 [ 32%]
tests\test_inheritance.py .....                                                                                  [ 34%]
tests\test_labels.py ..                                                                                          [ 34%]
tests\test_model_field_list.py ........                                                                          [ 38%]
tests\test_model_form_factory.py ............                                                                    [ 42%]
tests\test_model_form_field.py ...                                                                               [ 44%]
tests\test_phone_number.py ....                                                                                  [ 45%]
tests\test_phone_number_field.py ............                                                                    [ 50%]
tests\test_query_select_field.py .............                                                                   [ 55%]
tests\test_select_field.py ..........                                                                            [ 59%]
tests\test_synonym.py ..                                                                                         [ 60%]
tests\test_types.py ...............................................                                              [ 79%]
tests\test_unique_validator.py .......................                                                           [ 88%]
tests\test_utils.py .                                                                                            [ 89%]
tests\test_validators.py ...................                                                                     [ 96%]
tests\test_weekdays_field.py ..                                                                                  [ 97%]
tests\test_widgets.py ......                                                                                     [100%]

====================================================== FAILURES =======================================================
___________________________ TestModelFormConfiguration.test_supports_custom_datetime_format ___________________________

self = <tests.test_configuration.TestModelFormConfiguration object at 0x000002698E6B1790>

    def test_supports_custom_datetime_format(self):
        self.init(sa.DateTime, nullable=False)

        class ModelTestForm(ModelForm):
            class Meta:
                model = self.ModelTest
                datetime_format = '%Y-%m-%dT%H:%M:%S'

        form = ModelTestForm()
>       assert form.test_column.format == '%Y-%m-%dT%H:%M:%S'
E       AssertionError: assert ['%Y-%m-%dT%H:%M:%S'] == '%Y-%m-%dT%H:%M:%S'
E        +  where ['%Y-%m-%dT%H:%M:%S'] = <wtforms_components.fields.html5.DateTimeField object at 0x000002698EAFB550>.format
E        +    where <wtforms_components.fields.html5.DateTimeField object at 0x000002698EAFB550> = <tests.test_configuration.TestModelFormConfiguration.test_supports_custom_datetime_format.<locals>.ModelTestForm object at 0x000002698EAFB310>.test_column

tests\test_configuration.py:154: AssertionError
================================================== warnings summary ===================================================
tests\test_unique_validator.py:12
  C:\Users\Vidminas\GitHub\wtforms-alchemy\tests\test_unique_validator.py:12: MovedIn20Warning: Deprecated API features detected! These feature(s) are not compatible with SQLAlchemy 2.0. To prevent incompatible upgrades prior to updating applications, ensure requirements files are pinned to "sqlalchemy<2.0". Set environment variable SQLALCHEMY_WARN_20=1 to show all deprecation warnings.  Set environment variable SQLALCHEMY_SILENCE_UBER_WARNING=1 to silence this message. (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)
    base = declarative_base()

tests/test_model_form_factory.py::TestModelFormFactory::test_class_meta_wtforms2
tests/test_model_form_factory.py::TestModelFormFactory::test_class_meta_wtforms2
  C:\Users\Vidminas\GitHub\wtforms-alchemy\tests\test_model_form_factory.py:94: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    if LooseVersion(wtforms.__version__) < LooseVersion('2'):

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=============================================== short test summary info ===============================================
FAILED tests/test_configuration.py::TestModelFormConfiguration::test_supports_custom_datetime_format - AssertionError...
====================================== 1 failed, 248 passed, 3 warnings in 6.06s ======================================
sqlalchemy14: exit 1 (11.44 seconds) C:\Users\Vidminas\GitHub\wtforms-alchemy> py.test pid=12208
.pkg: _exit> python C:\Users\Vidminas\.conda\envs\wtforms-alchemy\Lib\site-packages\pyproject_api\_backend.py True setuptools.build_meta __legacy__
  sqlalchemy14: FAIL code 1 (238.39=setup[127.99]+cmd[98.97,11.44] seconds)
  evaluation failed :( (238.91 seconds)

(There is also an irrelevant unit test failing in both cases, which I've reported in https://github.com/kvesteri/wtforms-alchemy/issues/163)

Vidminas commented 1 year ago

With more thorough testing, I realised that since sa.Enum inherits from sa.String, the Length validator gets assigned to enum fields too, which leads to a crash when validating a form that contains enums, because they have no length.

Now patched and added a unit test too. tox -e sqlalchemy14 before the change:

================================================= test session starts =================================================
platform win32 -- Python 3.11.4, pytest-7.4.0, pluggy-1.2.0
cachedir: .tox\sqlalchemy14\.pytest_cache
rootdir: C:\Users\Vidminas\GitHub\wtforms-alchemy
plugins: cov-4.1.0
collected 250 items

tests\test_class_map.py .................                                                                        [  6%]
tests\test_column_aliases.py .....                                                                               [  8%]
tests\test_configuration.py ...........F......                                                                   [ 16%]
tests\test_country_field.py ..                                                                                   [ 16%]
tests\test_custom_fields.py .                                                                                    [ 17%]
tests\test_deep_form_relations.py ..                                                                             [ 18%]
tests\test_descriptions.py ..                                                                                    [ 18%] 
tests\test_field_exclusion.py ....                                                                               [ 20%]
tests\test_field_order.py .                                                                                      [ 20%]
tests\test_field_parameters.py .............                                                                     [ 26%]
tests\test_field_trimming.py ..                                                                                  [ 26%]
tests\test_form_meta.py ........                                                                                 [ 30%]
tests\test_hybrid_properties.py ..                                                                               [ 30%]
tests\test_i18n_extension.py ...                                                                                 [ 32%]
tests\test_inheritance.py .....                                                                                  [ 34%] 
tests\test_labels.py ..                                                                                          [ 34%]
tests\test_model_field_list.py ........                                                                          [ 38%]
tests\test_model_form_factory.py ............                                                                    [ 42%]
tests\test_model_form_field.py ...                                                                               [ 44%]
tests\test_phone_number.py ....                                                                                  [ 45%]
tests\test_phone_number_field.py ............                                                                    [ 50%]
tests\test_query_select_field.py .............                                                                   [ 55%]
tests\test_select_field.py ..........                                                                            [ 59%]
tests\test_synonym.py ..                                                                                         [ 60%]
tests\test_types.py ...............................................                                              [ 79%]
tests\test_unique_validator.py .......................                                                           [ 88%]
tests\test_utils.py .                                                                                            [ 88%]
tests\test_validators.py ...................F                                                                    [ 96%]
tests\test_weekdays_field.py ..                                                                                  [ 97%] 
tests\test_widgets.py ......                                                                                     [100%]

====================================================== FAILURES ======================================================= 
___________________________ TestModelFormConfiguration.test_supports_custom_datetime_format ___________________________ 

self = <tests.test_configuration.TestModelFormConfiguration object at 0x00000296FAA57990>

    def test_supports_custom_datetime_format(self):
        self.init(sa.DateTime, nullable=False)

        class ModelTestForm(ModelForm):
            class Meta:
                model = self.ModelTest
                datetime_format = '%Y-%m-%dT%H:%M:%S'

        form = ModelTestForm()
>       assert form.test_column.format == '%Y-%m-%dT%H:%M:%S'
E       AssertionError: assert ['%Y-%m-%dT%H:%M:%S'] == '%Y-%m-%dT%H:%M:%S'
E        +  where ['%Y-%m-%dT%H:%M:%S'] = <wtforms_components.fields.html5.DateTimeField object at 0x00000296FAEE6250>.format
E        +    where <wtforms_components.fields.html5.DateTimeField object at 0x00000296FAEE6250> = <tests.test_configuration.TestModelFormConfiguration.test_supports_custom_datetime_format.<locals>.ModelTestForm object at 0x00000296FADF7250>.test_column

tests\test_configuration.py:154: AssertionError
___________________________________ TestAutoAssignedValidators.test_enum_validators ___________________________________ 

self = <tests.test_validators.TestAutoAssignedValidators object at 0x00000296FAED3790>

    def test_enum_validators(self):
        class TestEnum(enum.Enum):
            A = 'a'
            B = 'b'

        self.init(type_=sa.Enum(TestEnum), nullable=True)
        form = self.form_class()

>       assert len(form.test_column.validators) == 1
E       assert 2 == 1
E        +  where 2 = len([<wtforms.validators.Optional object at 0x00000296FBA101D0>, <wtforms.validators.Length object at 0x00000296FBB40C10>])
E        +    where [<wtforms.validators.Optional object at 0x00000296FBA101D0>, <wtforms.validators.Length object at 0x00000296FBB40C10>] = <wtforms_components.fields.select.SelectField object at 0x00000296FBB40ED0>.validators
E        +      where <wtforms_components.fields.select.SelectField object at 0x00000296FBB40ED0> = <tests.ModelFormTestCase.init_form.<locals>.ModelTestForm object at 0x00000296FBB39610>.test_column

tests\test_validators.py:278: AssertionError
================================================== warnings summary =================================================== 
tests\test_unique_validator.py:12
  C:\Users\Vidminas\GitHub\wtforms-alchemy\tests\test_unique_validator.py:12: MovedIn20Warning: Deprecated API features 
detected! These feature(s) are not compatible with SQLAlchemy 2.0. To prevent incompatible upgrades prior to updating applications, ensure requirements files are pinned to "sqlalchemy<2.0". Set environment variable SQLALCHEMY_WARN_20=1 to show all deprecation warnings.  Set environment variable SQLALCHEMY_SILENCE_UBER_WARNING=1 to silence this message. (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)
    base = declarative_base()

tests/test_model_form_factory.py::TestModelFormFactory::test_class_meta_wtforms2
tests/test_model_form_factory.py::TestModelFormFactory::test_class_meta_wtforms2
  C:\Users\Vidminas\GitHub\wtforms-alchemy\tests\test_model_form_factory.py:94: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    if LooseVersion(wtforms.__version__) < LooseVersion('2'):

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=============================================== short test summary info =============================================== 
FAILED tests/test_configuration.py::TestModelFormConfiguration::test_supports_custom_datetime_format - AssertionError...FAILED tests/test_validators.py::TestAutoAssignedValidators::test_enum_validators - assert 2 == 1
====================================== 2 failed, 248 passed, 3 warnings in 2.35s ====================================== 
sqlalchemy14: exit 1 (4.38 seconds) C:\Users\Vidminas\GitHub\wtforms-alchemy> py.test pid=21920
.pkg: _exit> python C:\Users\Vidminas\.conda\envs\wtforms-alchemy\Lib\site-packages\pyproject_api\_backend.py True setuptools.build_meta __legacy__
  sqlalchemy14: FAIL code 1 (102.83=setup[58.31]+cmd[40.14,4.38] seconds)
  evaluation failed :( (103.08 seconds)

And after the change:

================================================= test session starts =================================================
platform win32 -- Python 3.11.4, pytest-7.4.0, pluggy-1.2.0
cachedir: .tox\sqlalchemy14\.pytest_cache
rootdir: C:\Users\Vidminas\GitHub\wtforms-alchemy
plugins: cov-4.1.0
collected 250 items

tests\test_class_map.py .................                                                                        [  6%]
tests\test_column_aliases.py .....                                                                               [  8%]
tests\test_configuration.py ...........F......                                                                   [ 16%]
tests\test_country_field.py ..                                                                                   [ 16%]
tests\test_custom_fields.py .                                                                                    [ 17%] 
tests\test_deep_form_relations.py ..                                                                             [ 18%]
tests\test_descriptions.py ..                                                                                    [ 18%]
tests\test_field_exclusion.py ....                                                                               [ 20%]
tests\test_field_order.py .                                                                                      [ 20%]
tests\test_field_parameters.py .............                                                                     [ 26%]
tests\test_field_trimming.py ..                                                                                  [ 26%]
tests\test_form_meta.py ........                                                                                 [ 30%]
tests\test_hybrid_properties.py ..                                                                               [ 30%]
tests\test_i18n_extension.py ...                                                                                 [ 32%]
tests\test_inheritance.py .....                                                                                  [ 34%]
tests\test_labels.py ..                                                                                          [ 34%]
tests\test_model_field_list.py ........                                                                          [ 38%]
tests\test_model_form_factory.py ............                                                                    [ 42%]
tests\test_model_form_field.py ...                                                                               [ 44%]
tests\test_phone_number.py ....                                                                                  [ 45%]
tests\test_phone_number_field.py ............                                                                    [ 50%]
tests\test_query_select_field.py .............                                                                   [ 55%]
tests\test_select_field.py ..........                                                                            [ 59%]
tests\test_synonym.py ..                                                                                         [ 60%]
tests\test_types.py ...............................................                                              [ 79%]
tests\test_unique_validator.py .......................                                                           [ 88%]
tests\test_utils.py .                                                                                            [ 88%]
tests\test_validators.py ....................                                                                    [ 96%]
tests\test_weekdays_field.py ..                                                                                  [ 97%] 
tests\test_widgets.py ......                                                                                     [100%]

====================================================== FAILURES ======================================================= 
___________________________ TestModelFormConfiguration.test_supports_custom_datetime_format ___________________________ 

self = <tests.test_configuration.TestModelFormConfiguration object at 0x0000024B3F9B3C50>

    def test_supports_custom_datetime_format(self):
        self.init(sa.DateTime, nullable=False)

        class ModelTestForm(ModelForm):
            class Meta:
                model = self.ModelTest
                datetime_format = '%Y-%m-%dT%H:%M:%S'

        form = ModelTestForm()
>       assert form.test_column.format == '%Y-%m-%dT%H:%M:%S'
E       AssertionError: assert ['%Y-%m-%dT%H:%M:%S'] == '%Y-%m-%dT%H:%M:%S'
E        +  where ['%Y-%m-%dT%H:%M:%S'] = <wtforms_components.fields.html5.DateTimeField object at 0x0000024B3FE21490>.format
E        +    where <wtforms_components.fields.html5.DateTimeField object at 0x0000024B3FE21490> = <tests.test_configuration.TestModelFormConfiguration.test_supports_custom_datetime_format.<locals>.ModelTestForm object at 0x0000024B3F94F390>.test_column

tests\test_configuration.py:154: AssertionError
================================================== warnings summary =================================================== 
tests\test_unique_validator.py:12
  C:\Users\Vidminas\GitHub\wtforms-alchemy\tests\test_unique_validator.py:12: MovedIn20Warning: Deprecated API features 
detected! These feature(s) are not compatible with SQLAlchemy 2.0. To prevent incompatible upgrades prior to updating applications, ensure requirements files are pinned to "sqlalchemy<2.0". Set environment variable SQLALCHEMY_WARN_20=1 to show all deprecation warnings.  Set environment variable SQLALCHEMY_SILENCE_UBER_WARNING=1 to silence this message. (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)
    base = declarative_base()

tests/test_model_form_factory.py::TestModelFormFactory::test_class_meta_wtforms2
tests/test_model_form_factory.py::TestModelFormFactory::test_class_meta_wtforms2
  C:\Users\Vidminas\GitHub\wtforms-alchemy\tests\test_model_form_factory.py:94: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    if LooseVersion(wtforms.__version__) < LooseVersion('2'):

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=============================================== short test summary info =============================================== 
FAILED tests/test_configuration.py::TestModelFormConfiguration::test_supports_custom_datetime_format - AssertionError...====================================== 1 failed, 249 passed, 3 warnings in 2.73s ====================================== 
sqlalchemy14: exit 1 (4.81 seconds) C:\Users\Vidminas\GitHub\wtforms-alchemy> py.test pid=8636
.pkg: _exit> python C:\Users\Vidminas\.conda\envs\wtforms-alchemy\Lib\site-packages\pyproject_api\_backend.py True setuptools.build_meta __legacy__
  sqlalchemy14: FAIL code 1 (96.98=setup[49.86]+cmd[42.31,4.81] seconds)
  evaluation failed :( (97.23 seconds)
Vidminas commented 1 year ago

With my above workarounds, rendering and validation no longer crash, but validation still fails for enum fields with "Not a valid choice" because the built-in wtforms_components SelectField does not like the trick of reversing values and labels:

From wtforms_components/fields/select.py, line 54:

@property
def choice_values(self):
    values = []
    for value, label in self.concrete_choices:
        if isinstance(label, (list, tuple)):
            for subvalue, sublabel in label:
                values.append(subvalue)
        else:
            values.append(value)
    return values

def pre_validate(self, form):
    """
    Don't forget to validate also values from embedded lists.
    """
    values = self.choice_values
    if (self.data is None and u'' in values) or self.data in values:
        return True

    raise ValidationError(self.gettext(u'Not a valid choice'))

The exception gets raised, because (with my above example enum column) choices are [('Powder', <MaterialType.POWDER: 'Powder'>), ('Sand', <MaterialType.SAND: 'Sand'>), ('Gravel', <MaterialType.GRAVEL: 'Gravel'>), ('Boulder', <MaterialType.BOULDER: 'Boulder'>)] but choice_values are ['Powder', 'Sand', 'Gravel', 'Boulder'], whereas data is an instance of the enum like <MaterialType.POWDER: 'Powder'>

I added a SelectField extension that overrides the choice_values creation to use enum instances instead of string values.