Created
December 16, 2018 22:26
-
-
Save benmoose/4fc2434b9a70fc8a8a08c59a8dc95c5b to your computer and use it in GitHub Desktop.
Decorator which helps model external input to APIs
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 attr | |
def api_model(cls): | |
""" | |
API Model decorator for representing input from external sources. | |
It is primarily responsible for | |
- encoding incoming data in a class (for easy use in application code) | |
- providing methods to check the validity of data. | |
To activate validity checking, classes using this decorator should add the `get_validation_errors` | |
method. | |
""" | |
cls._from_dict = classmethod(api_model_from_dict) | |
if not hasattr(cls, "from_dict"): | |
cls.from_dict = classmethod(api_model_from_dict) | |
cls._from_db_model = classmethod(api_model_from_db_model) | |
if not hasattr(cls, "from_db_model"): | |
cls.from_db_model = classmethod(api_model_from_db_model) | |
cls._to_dict = api_model_to_dict | |
if not hasattr(cls, "to_dict"): | |
cls.to_dict = api_model_to_dict | |
if not hasattr(cls, "get_validation_errors"): | |
cls.get_validation_errors = lambda _: [] | |
cls._is_valid = property(api_model_is_valid) | |
if not hasattr(cls, "is_valid"): | |
cls.is_valid = property(api_model_is_valid) | |
cls.__attrs_post_init__ = api_model_post_init | |
return attr.s( | |
cls, | |
these=api_model_get_ib_fields(cls.__slots__), | |
slots=True, | |
weakref_slot=False, | |
) | |
def api_model_post_init(self): | |
self.validation_errors = self.get_validation_errors() | |
if hasattr(self, "__post_init__"): | |
self.__post_init__() | |
def api_model_get_ib_fields(fields): | |
mapping = {field: attr.ib(default=None) for field in fields} | |
mapping["validation_errors"] = attr.ib(factory=list, repr=False) | |
return mapping | |
def api_model_to_dict(self, keep_empty_fields=False): | |
return attr.asdict( | |
self, filter=lambda k, v: _filter_attributes(k, v, keep_empty_fields) | |
) | |
def api_model_from_dict(cls, data): | |
args = {} | |
for attribute in cls.__slots__: | |
value = data.get(attribute) | |
if value is not None: | |
args[attribute] = value | |
return cls(**args) | |
def api_model_from_db_model(cls, db_model): | |
args = {} | |
for attribute in cls.__slots__: | |
value = getattr(db_model, attribute, None) | |
if value is not None: | |
args[attribute] = value | |
return cls(**args) | |
def api_model_is_valid(self): | |
return not bool(self.validation_errors) | |
def _filter_attributes(attribute, value): | |
return attribute.repr and value is not None | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment