import datetime from typing import Sequence from flask_wtf import FlaskForm from sqlalchemy.inspection import inspect from werkzeug.datastructures import ImmutableMultiDict from atlas_um.helpers.common import safe_strip from atlas_um.pgdb import pgdb class BaseForm(FlaskForm): """ Base form, extended with some helpful features. Allows comparison by data and provides some default values. """ FIELD_REQUIRED_MESSAGE = "This field is required" class Meta: def bind_field(self, form, unbound_field, options): filters = unbound_field.kwargs.get("filters", []) if safe_strip not in filters: filters.append(safe_strip) return unbound_field.bind(form=form, filters=filters, **options) def __init__(self, *args, **kwargs): self._obj = kwargs.get("obj") super().__init__(*args, **kwargs) def __eq__(self, other): try: return self.data == other.data and self.obj == other.obj except Exception: return False @property def obj(self): return self._obj def dump( self, cleanup: bool = False, fields: Sequence[str] = None, exclude: Sequence[str] = None, cleanup_fields: Sequence[str] = None, ) -> ImmutableMultiDict: """ Helper method to dump form as request body. Allows to cleanup values and respects relations fields and their usage in selects. """ fields = fields or [] exclude = exclude or [] cleanup_fields = cleanup_fields or [] data = {} for k, v in self.data.items(): if (fields and k not in fields) or (exclude and k in exclude): continue if isinstance(v, pgdb.Model): identity = inspect(v).identity if cleanup or k in cleanup_fields: data[k] = "__None" else: data[k] = str(identity[0]) if identity else "__None" elif isinstance(v, datetime.date): data[k] = v.strftime("%m/%d/%y") else: if cleanup or k in cleanup_fields: data[k] = "" else: data[k] = str(v) if v else "" return ImmutableMultiDict(data)