Commit 29f17f21 authored by PJ Janse van Rensburg's avatar PJ Janse van Rensburg

Separate model-conversion methods for choice fields.

parent 5066d6d2
import warnings import warnings
from enum import Enum, EnumMeta from enum import Enum, EnumMeta
import inspect
from wtforms import fields, validators from wtforms import fields, validators
from sqlalchemy import Boolean, Column from sqlalchemy import Boolean, Column
...@@ -244,9 +245,8 @@ class AdminModelConverter(ModelConverterBase): ...@@ -244,9 +245,8 @@ class AdminModelConverter(ModelConverterBase):
if override: if override:
return override(**kwargs) return override(**kwargs)
# Check choices # Check if a list of 'form_choices' are specified
form_choices = getattr(self.view, 'form_choices', getattr(self.view, 'choices', None)) form_choices = getattr(self.view, 'form_choices', None)
if mapper.class_ == self.view.model and form_choices: if mapper.class_ == self.view.model and form_choices:
choices = form_choices.get(prop.key) choices = form_choices.get(prop.key)
if choices: if choices:
...@@ -264,7 +264,6 @@ class AdminModelConverter(ModelConverterBase): ...@@ -264,7 +264,6 @@ class AdminModelConverter(ModelConverterBase):
return converter(model=model, mapper=mapper, prop=prop, return converter(model=model, mapper=mapper, prop=prop,
column=column, field_args=kwargs) column=column, field_args=kwargs)
return None return None
@classmethod @classmethod
...@@ -272,36 +271,54 @@ class AdminModelConverter(ModelConverterBase): ...@@ -272,36 +271,54 @@ class AdminModelConverter(ModelConverterBase):
if hasattr(column.type, 'length') and isinstance(column.type.length, int) and column.type.length: if hasattr(column.type, 'length') and isinstance(column.type.length, int) and column.type.length:
field_args['validators'].append(validators.Length(max=column.type.length)) field_args['validators'].append(validators.Length(max=column.type.length))
@converts('String', 'ChoiceType') # includes VARCHAR, CHAR, and Unicode @converts('String') # includes VARCHAR, CHAR, and Unicode
def conv_String(self, column, field_args, **extra): def conv_String(self, column, field_args, **extra):
available_choices = [] if column.nullable:
if hasattr(column.type, 'enums'): filters = field_args.get('filters', [])
available_choices = [(f, f) for f in column.type.enums] filters.append(lambda x: x or None)
elif hasattr(column.type, 'choices'): field_args['filters'] = filters
if isinstance(column.type.choices, EnumMeta):
available_choices = [(str(f.value), f.name) for f in column.type.choices]
else:
available_choices = column.type.choices
if available_choices:
field_args['choices'] = available_choices
accepted_values = [key for key, val in available_choices]
if column.nullable: self._string_common(column=column, field_args=field_args, **extra)
field_args['allow_blank'] = column.nullable return fields.StringField(**field_args)
accepted_values.append(None)
@converts('sqlalchemy.sql.sqltypes.Enum')
def convert_enum(self, column, field_args, **extra):
available_choices = [(f, f) for f in column.type.enums]
accepted_values = [key for key, val in available_choices]
field_args['validators'].append(validators.AnyOf(accepted_values)) if column.nullable:
field_args['coerce'] = lambda v: v.name if isinstance(v, Enum) else (v.code if isinstance(v, Choice) else text_type(v)) field_args['allow_blank'] = column.nullable
accepted_values.append(None)
filters = field_args.get('filters', [])
filters.append(lambda x: x or None)
field_args['filters'] = filters
field_args['choices'] = available_choices
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)
return form.Select2Field(**field_args) @converts('sqlalchemy_utils.types.choice.ChoiceType')
def convert_choice_type(self, column, field_args, **extra):
available_choices = []
# choices can either be specified as an enum, or as a list of tuples
if isinstance(column.type.choices, EnumMeta):
available_choices = [(f.value, f.name) for f in column.type.choices]
else:
available_choices = column.type.choices
accepted_values = [key for key, val in available_choices]
if column.nullable: if column.nullable:
field_args['allow_blank'] = column.nullable
accepted_values.append(None)
filters = field_args.get('filters', []) filters = field_args.get('filters', [])
filters.append(lambda x: x or None) filters.append(lambda x: x or None)
field_args['filters'] = filters field_args['filters'] = filters
self._string_common(column=column, field_args=field_args, **extra) field_args['choices'] = available_choices
return fields.StringField(**field_args) field_args['validators'].append(validators.AnyOf(accepted_values))
field_args['coerce'] = choice_type_coerce_factory(column.type)
return form.Select2Field(**field_args)
@converts('Text', 'LargeBinary', 'Binary', 'CIText') # includes UnicodeText @converts('Text', 'LargeBinary', 'Binary', 'CIText') # includes UnicodeText
def conv_Text(self, field_args, **extra): def conv_Text(self, field_args, **extra):
...@@ -375,6 +392,29 @@ class AdminModelConverter(ModelConverterBase): ...@@ -375,6 +392,29 @@ class AdminModelConverter(ModelConverterBase):
def convert_JSON(self, field_args, **extra): def convert_JSON(self, field_args, **extra):
return form.JSONField(**field_args) return form.JSONField(**field_args)
def choice_type_coerce_factory(type_):
"""
Return a function needed to coerce a ChoiceTyped column. This function is
then passed to generated SelectField as the default coerce function.
:param type_: ChoiceType object
"""
choices = type_.choices
if (
# Enum is not None and
isinstance(choices, type)
and issubclass(choices, Enum)
):
key, choice_cls = 'value', choices
else:
key, choice_cls = 'code', Choice
def choice_coerce(value):
if value is None:
return None
if isinstance(value, choice_cls):
return getattr(value, key)
return type_.python_type(value)
return choice_coerce
def _resolve_prop(prop): def _resolve_prop(prop):
""" """
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment