Created
July 31, 2019 11:10
-
-
Save JellyWX/af2052c03f07f9fa4e1e65e506f7703e to your computer and use it in GitHub Desktop.
Small form modelling system for Flask
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import typing | |
from werkzeug.security import generate_password_hash, check_password_hash | |
from werkzeug import ImmutableMultiDict | |
class Field(): | |
class FieldLengthExceeded(Exception): | |
pass | |
class MissingFieldData(Exception): | |
pass | |
def __init__(self, name: str, *, strict: bool=False, max_length: int=1024): | |
self.name: str = name | |
self._raw_value: typing.Optional[str] = None | |
self._strict: bool = strict | |
self._max_length: int = max_length | |
@property | |
def value(self) -> typing.Optional[str]: | |
return self._value | |
@value.setter | |
def value(self, value): | |
if value is not None and len(value) > self._max_length: | |
raise self.FieldLengthExceeded | |
else: | |
self._value = value | |
def validate(self): | |
if self._raw_value is None and self._strict: | |
raise self.MissingFieldData | |
elif self._value is not None: | |
self._type_specific_validation() | |
def _type_specific_validation(self): | |
pass | |
class IntegerField(Field): | |
class InvalidInteger(Exception): | |
pass | |
def _type_specific_validation(self): | |
if not all([x in '0123456789' for x in self._value]): | |
raise self.InvalidInteger | |
@Field.value.getter | |
def value(self) -> typing.Optional[int]: | |
return self._value if self._value is None else int(self._value) | |
class PasswordField(Field): | |
class Password(str): | |
def __eq__(self, comparator): | |
return check_password_hash(self, comparator) | |
@Field.value.getter | |
def value(self) -> typing.Optional[Password]: | |
return self._value if self._value is None else self.Password(generate_password_hash(self._value)) | |
class EmailField(Field): | |
class InvalidEmail(Exception): | |
pass | |
def _type_specific_validation(self): | |
if '@' not in self._value: | |
raise self.InvalidEmail | |
class FormDeserializer(): | |
def __init__(self, data: ImmutableMultiDict): | |
for attrib in dir(self): | |
d = getattr(self, attrib) | |
if isinstance(d, Field): | |
# Get the attribute from the form data | |
v: typing.Optional[str] = data.get(d.name, None) | |
d.value = v | |
# Check the value is valid | |
d.validate() | |
# Finally, rebind to the value of the field | |
self.__dict__[attrib] = d.value |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment