text: from __future__ import absolute_import
  from __future__ import unicode_literals
  
  import functools
  import inspect
  import logging
  from operator import attrgetter
  
  import flask
  from decimal import Decimal
  from flask_wtf import FlaskForm as BaseForm
  from keg.db import db
  import sqlalchemy as sa
  from markupsafe import Markup
  from sqlalchemy_utils import ArrowType, get_class_by_table
  import six
  import wtforms.fields
  import wtforms.form
  from wtforms.validators import InputRequired, Optional, StopValidation, NumberRange
  from wtforms_alchemy import (
      FormGenerator as FormGeneratorBase,
      model_form_factory,
      model_form_meta_factory,
  )
  from wtforms_components.fields import (
      SelectField as SelectFieldBase,
      SelectMultipleField as SelectMultipleFieldBase,
  )
  
  from keg_elements.db.columns import DBEnum
  from keg_elements.db.utils import has_column
  from keg_elements.extensions import lazy_gettext as _
  from keg_elements.forms.validators import NumberScale
  
  form_element = flask.Blueprint('form_element', __name__)
  log = logging.getLogger(__name__)
  
  
  def to_title_case(x):
      """ underscore or dash to title case notation """
      return x.replace('_', ' ').replace('-', ' ').title()
  
  
  # sentinel
  _not_given = ()
  
  
  class FieldMeta(object):
      """Meta information for fields to override model-generated info.
  
      Rather than forcing all-or-nothing acceptance of model-generated meta info from wtforms,
      FieldMeta may be provided in the FieldsMeta nested class of the form to override specifics.
  
      All modifications are applied to the field instance during the form generation process.
  
      Example::
  
          class PersonForm(ModelForm):
              class Meta:
                  model = Person
  
              class FieldsMeta:
                  name = FieldMeta('Full Name')
  
      :param label_text: Force a label value.
      :param description: Force a description value.
      :param label_modifier: Callable to be called with the default label. label_text takes
          precedence if both are provided. But, this modifier will apply to choices as well if
          applicable and a choices_modifier is not given.
      :param choices_modifier: Callable to be called with the label value for choices.
      :param required: Force validators to be added/removed for requirement.
      :param widget: Force a specific widget to be used for the field in render.
      :param extra_validators: Add the given validators to those existing on the field.
      :param coerce: Applies a specific coerce for field values. Applicable to select fields only.
      :param default: Forces a default value on the field.
      """
  
      def __init__(self, label_text=_not_given, description=_not_given, label_modifier=_not_given,
                   choices_modifier=_not_given, choices=None, required=_not_given, widget=_not_given,
                   extra_validators=tuple(), coerce=_not_given, default=_not_given):
          self.label_text = label_text
          self.label_modifier = label_modifier
          self.description = description
          self.choices_modifier = choices_modifier
          self.choices = choices
          self.required = required
          self.widget = widget
          self.extra_validators = extra_validators
          self.coerce = coerce
          self.default = default
  
          assert self.required in (_not_given, False, True)
  
      def apply_to_field(self, field):
          # field is a wtforms.fields.core.UnboundField instance
          self.apply_to_label(field)
          self.apply_to_description(field)
          self.apply_to_choices(field)
          self.apply_required(field)
          self.apply_widget(field)
          self.apply_extra_validators(field)
          self.apply_coerce(field)
          self.apply_default(field)
  
      def apply_to_label(self, field):
          default_label = field.kwargs['label']
          if self.label_text is not _not_given:
              label_text = self.label_text
          elif self.label_modifier is None:
              label_text = default_label
          elif self.label_modifier is _not_given:
              label_text = to_title_case(default_label)
          else:
              label_text = self.label_modifier(default_label)
          field.kwargs['label'] = self.modify_label(label_text)
  
      def modify_label(self, label_text):
          """ for subclasses to easily modify the final label text value """
          return label_text
  
      def apply_to_description(self, field):
          default_description = field.kwargs.get('description')
          if self.description is _not_given:
              description = default_description
          else:
              description = self.description
          field.kwargs['description'] = self.modify_description(description)
  
      def modify_description(self, description):
          """ for subclasses to easily modify the final label text value """
          return description
  
      def apply_to_choices(self, field):
          default_choices = field.kwargs.get('choices', None)
          if default_choices is None:
              # this isn't a field that has choices
              return
  
          if self.choices_modifier is None:
              modifier = None
          elif self.choices_modifier is not _not_given:
              modifier = self.choices_modifier
          elif self.label_modifier is None:
              # no choices modifier and the label modifier is explicit, so no label modifier
              modifier = None
          elif self.label_modifier is _not_given:
              # title case to labels by default
              modifier = to_title_case
          else:
              # a label modifier was given, use that since no choices modifier was given to override
              modifier = self.label_modifier
  
          if self.choices is not None:
              choices = self.choices
          elif modifier is None:
              choices = default_choices
          else:
              choices = [(v, modifier(l)) for v, l in default_choices]
  
          field.kwargs['choices'] = self.modify_choices(choices)
  
      def modify_choices(self, choices):
          return choices
  
      def apply_coerce(self, field):
          if self.coerce is _not_given:
              return
          if not issubclass(field.field_class, wtforms.SelectField):
              raise ValueError('`coerce` argument may only be used for select fields')
          field.kwargs['coerce'] = self.coerce
  
      def apply_default(self, field):
          if self.default is _not_given:
              return
          field.kwargs['default'] = self.default
  
      def apply_required(self, field):
          validators = field.kwargs.get('validators', [])
  
          if self.required == _not_given:
              # required value not given on FieldMeta, don't make any changes
              pass
          elif self.required:
              # If a required validator isn't present, we need to add one.
              req_val_test = lambda val: hasattr(val, 'field_flags') and 'required' in val.field_flags
              if not list(filter(req_val_test, validators)):
                  validators.append(InputRequired())
  
              # If an optional validator is present, we need to remove it.
              not_opt_val_test = lambda val: not hasattr(val, 'field_flags') or \
                  'optional' not in val.field_flags
              not_opt_validators = list(filter(not_opt_val_test, validators))
              field.kwargs['validators'] = not_opt_validators
          else:
              # If an optional validator isn't present, we need to add one.
              opt_val_test = lambda val: hasattr(val, 'field_flags') and 'optional' in val.field_flags
              if not list(filter(opt_val_test, validators)):
                  validators.append(Optional())
  
              # If a required validator is present, we need to remove it.
              non_req_val_test = lambda val: not hasattr(val, 'field_flags') or \
                  'required' not in val.field_flags
              not_req_validators = list(filter(non_req_val_test, validators))
              field.kwargs['validators'] = not_req_validators
  
      def apply_widget(self, field):
          if self.widget != _not_given:
              field.kwargs['widget'] = self.widget
  
      def apply_extra_validators(self, field):
          field.kwargs.setdefault('validators', [])
          field.kwargs['validators'] += self.extra_validators
  
  
  def select_coerce(es_pass_thru, coerce, value):
      if es_pass_thru and value == '':
          return value
      if coerce is not _not_given:
          return coerce(value)
  
      # try coercing to int first.  If not valid, fall back to default behavior
      try:
          return int(value)
      except ValueError as e:
          if 'invalid literal for int()' not in six.text_type(e):
              raise
          return six.text_type(value)
  
  
  class SelectMixin:
      def __init__(self, *args, **kwargs):
          self.add_blank_choice = kwargs.pop('add_blank_choice', True)
          coerce_arg = kwargs.pop('coerce', _not_given)
          super().__init__(*args, **kwargs)
  
          if self.add_blank_choice:
              # If we are adding a blank choice, and it is selected, we want the value that comes back
              # in .data to be None -> as if no value was selected.
              #
              # self.filters is a tuple, so have to do some extra work.
              self.filters = [lambda x: None if x == '' else x] + list(self.filters)
  
          self.coerce = functools.partial(select_coerce, self.add_blank_choice, coerce_arg)
  
      def iter_choices(self):
          if self.add_blank_choice:
              yield ('', '', (self.coerce, False))
          for value in super().iter_choices():
              yield value
  
      @property
      def choice_values(self):
          values = super().choice_values
          if self.add_blank_choice:
              return [''] + values
          return values
  
      @property
      def selected_choice_label(self):
          value_dict = dict(self.concrete_choices)
          return value_dict.get(self.data)
  
  
  class SelectField(SelectMixin, SelectFieldBase):
      """
          Provides helpful features above wtforms_components SelectField which it is based on:
  
          1) Adds a blank choice by default at the front of the choices.  This results in your user
             being forced to select something if the field is required, which avoids initial
             defaulting of the first value in the field getting submitted.
          2) The coerce function used for the choices will automatically convert to int if possible,
             falling back to unicode if the value is not an integer.
      """
  
  
  class SelectMultipleField(SelectMixin, SelectMultipleFieldBase):
      """
          Provides helpful features above wtforms_components SelectMultipleField which it is
          based on:
  
          The coerce function used for the choices will automatically convert to int if possible,
          falling back to unicode if the value is not an integer.
      """
      def __init__(self, *args, **kwargs):
          kwargs['add_blank_choice'] = kwargs.get('add_blank_choice', False)
          super().__init__(*args, **kwargs)
  
  
  class MultiCheckboxField(wtforms.fields.SelectMultipleField):
      """
      A multiple-select, except displays a list of checkboxes.
      """
  
  
  class RequiredBoolRadioField(wtforms.fields.RadioField):
      """A radio group field with true/false labels and a required validator.
  
      :param true_label: Optional, defaults to Yes.
      :param false_label: Optional, defaults to No.
      :param validators: Optional. Any provided validators will be added to InputRequired.
      """
      def __init__(self, *args, **kwargs):
          true_label = kwargs.pop('true_label', _('Yes'))
          false_label = kwargs.pop('false_label', _('No'))
  
          def bool_coerce(val):
              if val == u'True':
                  return True
              if val == u'False':
                  return False
              return val
  
          kwargs['choices'] = [(True, true_label), (False, false_label)]
          kwargs['coerce'] = bool_coerce
          kwargs['validators'] = [InputRequired()] + kwargs.get('validators', [])
  
          super(RequiredBoolRadioField, self).__init__(*args, **kwargs)
          self.type = 'RadioField'
  
  
  class RelationshipFieldBase:
      """Common base for single/multiple select fields that reference ORM relationships.
  
      Handles one-to-many and many-to-many patterns.
  
      Note, must use the wtforms-components fields as a base, because we depend on
      lazy-loaded choices. At import time, a field's choices may not be fully available
      in the data. In addition, when pairing the form with an existing record, we need
      to ensure that the option from the record is present even if it would normally
      be filtered (e.g. an inactive option).
      """
      def __init__(self, label=None, orm_cls=None, label_attr=None, fk_attr='id',
                   query_filter=None, coerce=_not_given, **kwargs):
          label = self.field_label_modifier(label)
          self.orm_cls = orm_cls
          self.label_attr = label_attr
          if self.label_attr is None:
              self.label_attr = self.get_best_label_attr()
          self.fk_attr = fk_attr
          self.query_filter = query_filter
          if not self.fk_attr and not coerce:
              def coerce_to_orm_obj(value):
                  """Coerce ID to relationship object."""
                  # If coming form formdata, we'll get a string ID.
                  if isinstance(value, str):
                      return self.orm_cls.query.get(value)
  
                  # If coming from object data, we'll get an ORM instance.
                  return value
  
              coerce = coerce_to_orm_obj
  
          super().__init__(label=label, choices=self.get_choices, coerce=coerce, **kwargs)
  
      def field_label_modifier(self, label):
          """Modifies the label to something more human-friendly.
  
          One-to-many relationships often have a field name like "foo_id", which title cases
          as "Foo Id". Form should only show "Foo", though, so we trim it that way here.
          """
          if label.lower().endswith(' id'):
              return label.rsplit(' ', 1)[0]
          return label
  
      def build_query(self):
          query = self.query_base()
          query = self.filter_query(query)
          return query
  
      def query_base(self):
          return self.orm_cls.query.order_by(self.label_attr)
  
      def get_data_filter(self):
          if self.fk_attr:
              return getattr(self.orm_cls, self.fk_attr) == self.data
          else:
              return self.orm_cls.id == self.data.id
  
      def filter_query(self, query):
          filter_terms = []
          # Get supplied filters.
          if callable(self.query_filter):
              filter_terms.append(self.query_filter(self))
          elif self.query_filter is not None:
              filter_terms.append(self.query_filter)
  
          # Having an existing value should filter the query.
          if self.data is not None:
              data_filter = self.get_data_filter()
              if data_filter is not None:
                  filter_terms.append(data_filter)
  
          # Apply filter terms with or_, or directly, depending on length.
          if len(filter_terms) == 1:
              query = query.filter(*filter_terms)
          elif len(filter_terms) > 1:
              query = query.filter(sa.sql.or_(*filter_terms))
  
          return query
  
      def get_best_label_attr(self):
          if has_column(self.orm_cls, 'label'):
              return 'label'
  
          if has_column(self.orm_cls, 'name'):
              return 'name'
  
          return None
  
      def get_option_label(self, obj):
          if self.label_attr:
              return getattr(obj, self.label_attr)
  
          return str(obj)
  
      def get_choices(self):
          query = self.build_query()
  
          def get_value(obj):
              if self.fk_attr:
                  return str(getattr(obj, self.fk_attr))
  
              return str(obj.id)
  
          return [(get_value(obj), self.get_option_label(obj)) for obj in query]
  
      @property
      def choice_values(self):
          # coerce values used for validation, because the data we're matching will
          # be int type
          return [self.coerce(v) for v in super().choice_values]
  
  
  class RelationshipField(RelationshipFieldBase, SelectField):
      """SelectField for relationships.
  
      Args:
  
          orm_cls (class): Model class of the relationship attribute. Used to query
          records for populating select options.
  
          relationship_attr (str): Name of the attribute on form model that refers to
          the relationship object. Typically this is a foreign key ID.
  
          label_attr (str): Name of attribute on relationship class to use for select
          option labels.
  
          fk_attr (str): Optional name of foreign key column of ORM class. If set to
          None, coerce values to instances of ORM class. Otherwise, coerce values to
          the attribute of ORM class the foreign key belongs to. Default is 'id'.
  
          query_filter (callable): Optional SA query filter criterion for querying select
          options. Can be a function that returns a filter criterion. Function is
          called with the RelationshipField instance it belongs to.
  
          coerce (callable): Optional function used to coerce form values. By default,
          if fk_attr is set to None, values are coerced to instances of ORM class.
          Otherwise, the default select coersion is applied. Setting this overrides
          default behavior.
  
          kwargs: Passed to ``SelectField.__init__``.
  
      Example::
          class Bar(Model):
              name = Column(Unicode(255))
              foos = relationship('Foo', foreign_keys='foos.id')
          class Foo(Model):
              name = Column(Unicode(255))
              bar_id = Column(sa.ForeignKey(Bar.id))
              bar = relationship(Bar, foreign_keys=bar_id)
          class FooForm(ModelForm):
              bar_id = RelationshipField('Bar Label', Bar, 'name')
      """
  
  
  class RelationshipMultipleField(RelationshipFieldBase, SelectMultipleField):
      """SelectMultipleField for relationships.
      Args:
  
          orm_cls (class): Model class of the relationship attribute. Used to query
          records for populating select options.
  
          relationship_attr (str): Name of the collection on form model that refers to
          the relationship object.
  
          label_attr (str): Name of attribute on relationship class to use for select
          option labels.
  
          query_filter (callable): Optional SA query filter criterion for querying select
          options. Can be a function that returns a filter criterion. Function is
          called with the RelationshipField instance it belongs to.
  
          coerce (callable): Optional function used to coerce form values. By default,
          values are coerced to instances of ORM class. Setting this overrides
          default behavior.
  
          kwargs: Passed to ``SelectMultipleField.__init__``.
  
      Example::
          class Bar(Model):
              name = Column(Unicode(255))
              foos = relationship('Foo', foreign_keys='foos.id')
          class Foo(Model):
              name = Column(Unicode(255))
              bar_id = Column(sa.ForeignKey(Bar.id))
              bar = relationship(Bar, foreign_keys=bar_id)
          class BarForm(ModelForm):
              foos = RelationshipMultipleField('Foos', Foo, 'name')
      """
      def __init__(self, label, orm_cls, label_attr=None,
                   query_filter=None, coerce=_not_given, **kwargs):
          super().__init__(label, orm_cls, label_attr, None, query_filter, coerce, **kwargs)
  
      def get_data_filter(self):
          if not self.data:
              # Empty set should not add any options. Returning in_ in this case has
              # undesirable results.
              return
          existing_ids = [obj.id for obj in self.data]
          return self.orm_cls.id.in_(existing_ids)
  
  
  class _TypeHintingTextInputBase(wtforms.widgets.TextInput):
      def __init__(self, prefix=None, suffix=None):
          self.prefix = prefix
          self.suffix = suffix
          super().__init__()
  
  
  class TypeHintingTextInputB3(_TypeHintingTextInputBase):
      """
      A text input widget with a prefix and/or suffix to hint at the expected type or units.
      For use with bootstrap 3
      """
      def __call__(self, field, **kwargs):
          def make_addon(txt):
              return Markup(
                  '<span class="input-group-addon">{}</span>'.format(wtforms.widgets.core.escape(txt))
              )
  
          return Markup(
              '<div class="input-group">{pre}{field}{post}</div>'.format(
                  pre=make_addon(self.prefix) if self.prefix else '',
                  field=super().__call__(field, **kwargs).__html__(),
                  post=make_addon(self.suffix) if self.suffix else ''
              )
          )
  
  
  class TypeHintingTextInputB4(_TypeHintingTextInputBase):
      """
      A text input widget with a prefix and/or suffix to hint at the expected type or units.
      For use with bootstrap 4
      """
      def __call__(self, field, **kwargs):
          def make_addon(txt, addon_type):
              return Markup(
                  '<div class="input-group-{type}">'
                  '   <span class="input-group-text">{txt}</span>'
                  "</div>".format(type=addon_type, txt=wtforms.widgets.core.escape(txt))
              )
  
          return Markup(
              '<div class="input-group">{pre}{field}{post}</div>'.format(
                  pre=make_addon(self.prefix, "prepend") if self.prefix else "",
                  field=super().__call__(field, **kwargs).__html__(),
                  post=make_addon(self.suffix, "append") if self.suffix else "",
              )
          )
  
  
  def _max_for_numeric(digits, scale):
      return Decimal('{}.{}'.format('9' * (digits - scale), '9' * scale))
  
  
  class FormGenerator(FormGeneratorBase):
      """Model form generator that applies field meta info, provides validators, etc.
  
      Meta nested class directives (in addition to wtforms-alchemy):
      - include_datetimes_with_default
      - include_required_foreign_keys
  
      Field class overrides:
      - Use our SelectField instead of the WTForms default
      - Use our RequiredBoolRadioField for non-nullable boolean fields
      - Use RelationshipField for foreign key fields
  
      Meta info modifiers:
      - Use FieldsMeta.<field_name> if provided
      - Falls back to FieldsMeta.__default__
      - If none of the above, uses a blank FieldsMeta object, which will title case the label.
  
      Validators:
      - Applies range/scale numeric validators when applicable.
      """
      def __init__(self, form_class):
          super(FormGenerator, self).__init__(form_class)
          self.fields_meta = getattr(self.form_class, 'FieldsMeta', None)
  
      def skip_column(self, column):
          # Verify the key is not also in exclude=[] so we don't break compatibility with forms
          # that already manually excluded these fields
          if (not self.meta.include_datetimes_with_default
                  and isinstance(column.type, ArrowType)
                  and column.default
                  and column.key not in self.meta.exclude):
              return True
  
          # include_foreign_keys will pull in all foreign keys on the object. If we want the
          # form to include only required keys, we use include_required_foreign_keys.
          include_required_fks = getattr(self.meta, 'include_required_foreign_keys', False)
          if (include_required_fks and column.foreign_keys and column.nullable is False):
              return False
  
          return super().skip_column(column)
  
      def get_field_class(self, column):
          field_cls = super(FormGenerator, self).get_field_class(column)
          if field_cls is SelectFieldBase:
              return SelectField
  
          is_required_boolean = (field_cls is wtforms.fields.BooleanField
                                 and not column.nullable
                                 and not column.default)
  
          if is_required_boolean:
              return RequiredBoolRadioField
  
          return field_cls
  
      def get_field_modifier(self, prop):
  
          # is there an entry in FieldsMeta?
          if hasattr(self.fields_meta, prop.key):
              field_modifier = getattr(self.fields_meta, prop.key)
          else:
              field_modifier = getattr(self.fields_meta, '__default__', _not_given)
              if field_modifier is _not_given:
                  field_modifier = FieldMeta
  
          return field_modifier() if inspect.isclass(field_modifier) else field_modifier
  
      def create_field(self, prop, column):
          if column.foreign_keys:
              foreign_key = next(iter(column.foreign_keys))
              orm_cls = get_class_by_table(db.Model, foreign_key.column.table)
              validators = self.create_validators(prop, column)
              field = RelationshipField(
                  label=to_title_case(str(column.key)),
                  orm_cls=orm_cls,
                  validators=validators,
              )
          else:
              field = super(FormGenerator, self).create_field(prop, column)
  
          modifier = self.get_field_modifier(prop)
          if modifier is not None:
              modifier.apply_to_field(field)
          return field
  
      def create_validators(self, prop, column):
          validators = super(FormGenerator, self).create_validators(prop, column)
          if isinstance(column.type, sa.Numeric) and not isinstance(column.type, sa.Float):
              if column.type.precision is None or column.type.scale is None:
                  raise ValueError('Numeric fields must specify precision and scale')
              max_ = _max_for_numeric(column.type.precision, column.type.scale)
              validators.append(NumberRange(min=-max_, max=max_))
              validators.append(NumberScale(column.type.scale))
          return validators
  
      def length_validator(self, column):
          if isinstance(column.type, sa.types.Enum):
              return None
          return super(FormGenerator, self).length_validator(column)
  
      def select_field_kwargs(self, column):
          enum_cls = getattr(column.type, 'enum_class', None)
          if enum_cls and issubclass(enum_cls, DBEnum):
              return {
                  'coerce': enum_cls.coerce,
                  'choices': enum_cls.form_options()
              }
          return super().select_field_kwargs(column)
  
  
  def field_to_dict(field):
      if isinstance(field, wtforms.fields.FormField):
          return form_fields_to_dict(field)
      if isinstance(field, wtforms.fields.FieldList):
          return [field_to_dict(subfield) for subfield in field]
      return {
          'data': field.data,
          'errors': field.errors,
          'label': field.label.text,
          'required': field.flags.required,
      }
  
  
  def form_fields_to_dict(form):
      return dict((six.text_type(name), field_to_dict(field))
                  for name, field in six.iteritems(form._fields))
  
  
  ___validator_creation_counter = 0
  
  
  def form_validator(func=None, only_when_fields_valid=False):
      """Decorator used to mark a method as a form level validator.
  
      :param only_when_fields_valid: Use to disable validator if form already has errors.
      """
      if func is None:
          return functools.partial(form_validator, only_when_fields_valid=only_when_fields_valid)
  
      @functools.wraps(func)
      def wrapper(form):
          if not only_when_fields_valid or not form.field_errors:
              return func(form)
  
      global ___validator_creation_counter
      wrapper.___form_validator = True
      ___validator_creation_counter += 1
      wrapper.___creation_counter = ___validator_creation_counter
      return wrapper
  
  
  class Form(BaseForm):
      """Base form with a bunch of QoL improvements
  
  
      :param _field_order: Relying on the default field ordering can lead to unintuitive forms. It is
          possible to override this by adding the ``_field_order`` class attribute. Set this class
          variable to a tuple or list of field names (addressable via Form._fields['name_of_field'])
          and the form will render in that order. You must include all the fields, except CSRF.
          Forgetting a field or adding one which doesn't exist will cause the form to raise a
          ``ValueError`` and the form will not be rendered.
  
              class MyForm(Form):
                  _field_order = ('field1', 'field2',)
  
                  field1 = String('field1_label')  # Note that we don't use the label in the ordering
                  field2 = String()
      """
      def __init__(self, *args, **kwargs):
          super(Form, self).__init__(*args, **kwargs)
          self._form_level_errors = []
          self._errors = None
          self.after_init(args, kwargs)
  
      def __iter__(self):
          order = getattr(self, '_field_order', None)
  
          if order is None:
              return super().__iter__()
  
          has_csrf = hasattr(self, 'csrf_token')
          order = (['csrf_token'] if has_csrf else []) + list(order)
  
          declared = set(self._fields.keys())
          ordered = set(order)
  
          if declared != ordered:
              not_ordered = declared - ordered
              extra_ordered = ordered - declared
              raise ValueError(
                  'Custom field ordering for {} is incorrect.'.format(self.__class__.__name__),
                  ' Missing fields: {} '.format(not_ordered),
                  ' Extra fields: {} '.format(extra_ordered),
              )
  
          return (self._fields[f] for f in order)
  
      def after_init(self, args, kwargs):
          """Hook for providing customization on the form after fields are initialized."""
          pass
  
      def fields_todict(self):
          """Turns a form into dicts and lists with both data and errors for each field."""
          return form_fields_to_dict(self)
  
      def validate(self):
          """Applies validators and returns bool.
  
          Methods decorated as form-level validators are run after WTForms generic validation.
          """
          fields_valid = super(Form, self).validate()
  
          form_validators = {}
          # Traverse the MRO so we can get validators in parent classes.
          # Do so in reverse order so child classes can override parents' validators.
          # WTForms will not include the methods on form instances so we get them from the classes.
          for cls in reversed(self.__class__.__mro__):
              cls_validators = {
                  name: attr for name, attr in six.iteritems(cls.__dict__)
                  if getattr(attr, '___form_validator', False)
              }
              form_validators.update(cls_validators)
  
          self._form_level_errors = []
          for validator in sorted(form_validators.values(), key=attrgetter('___creation_counter')):
              try:
                  validator(self)
              except StopValidation as e:
                  if e.args and e.args[0]:
                      self._form_level_errors.append(e.args[0])
                  break
              except ValueError as e:
                  self._form_level_errors.append(e.args[0])
  
          return fields_valid and not self._form_level_errors
  
      @property
      def form_errors(self):
          """Form-level validator errors will be logged in this list."""
          return self._form_level_errors
  
      @property
      def field_errors(self):
          """Field-level validator errors come from WTForms' errors."""
          return super().errors
  
      @property
      def errors(self):
          """Field-level errors, plus form-level errors under the key "_form"."""
          errors = self.field_errors
          if self.form_errors:
              errors['_form'] = self.form_errors
          return errors
  
  
  BaseModelFormMeta = model_form_meta_factory()
  
  
  class ModelFormMeta(BaseModelFormMeta):
      """Base model form metaclass that handles nested inheritance issues.
  
      The default metaclass here will handle the nested Meta class. A form
      subclass with a Meta nested class will treat the form's superclass' Meta
      as a parent.
  
      This metaclass does the same thing for FieldsMeta, allowing superclasses
      to define a FieldsMeta that may reasonably be passed down to the subclass.
      """
      def __init__(cls, *args, **kwargs):
          bases = []
          for class_ in cls.__mro__:
              if 'FieldsMeta' in class_.__dict__:
                  bases.append(getattr(class_, 'FieldsMeta'))
  
          if object not in bases:
              bases.append(object)
  
          cls.FieldsMeta = type('FieldsMeta', tuple(bases), {})
  
          BaseModelFormMeta.__init__(cls, *args, **kwargs)
  
  
  BaseModelForm = model_form_factory(Form, meta=ModelFormMeta, form_generator=FormGenerator)
  
  
  class ModelForm(BaseModelForm):
      """Base model-generated form class that applies KegElements generator and meta."""
      @classmethod
      def get_session(cls):
          return db.session
  