from datetime import date, datetime, time, timedelta
from json.encoder import JSONEncoder as BaseJSONEncoder
import re
from enum import Enum
from .fields import MultiTypeField
from .model_types import ListModel
from .models import BaseModel
__all__ = ['factory', 'JSONEncoder', 'Factory']
def underscore_to_camel(string):
"""
Converts underscored string to camel case.
"""
return re.sub('_([a-z])', lambda x: x.group(1).upper(), string)
class BaseModelIterator:
def __init__(self, model):
self.model = model
def __iter__(self):
fields = self.model.get_fields()
for fieldname in fields:
field = self.model.get_field_obj(fieldname)
name = self.model.get_real_name(fieldname)
yield name, field, self.model.get_field_value(fieldname)
class ModelIterator(BaseModelIterator):
"""
Helper in order to iterate over model fields.
"""
def __iter__(self):
for name, field, value in super(ModelIterator, self).__iter__():
yield name, value
items = __iter__
def values(self):
for _, value in self:
yield value
def keys(self):
for name, _ in self:
yield name
class BaseFormatterIter:
pass
class BaseFieldtypeFormatterIter(BaseFormatterIter):
def __init__(self, obj, field, parent_formatter):
self.obj = obj
self.field = field
self.parent_formatter = parent_formatter
class ListFormatterIter(BaseFieldtypeFormatterIter):
def __iter__(self):
for item in self.obj:
yield self.parent_formatter.format_field(self.field, item)
class BaseModelFormatterIter(BaseModelIterator, BaseFormatterIter):
"""
Base formatter iterator for Dirty Models.
"""
def __iter__(self):
for name, field, value in super(BaseModelFormatterIter, self).__iter__():
yield name, self.format_field(field,
self.model.get_field_value(name))
def format_field(self, field, value):
if isinstance(field, MultiTypeField):
return self.format_field(field.get_field_type_by_value(value), value)
elif isinstance(value, BaseModel):
return self.__class__(value)
elif isinstance(value, ListModel):
return ListFormatterIter(obj=value, field=value.get_field_type(), parent_formatter=self)
elif isinstance(value, Enum):
return self.format_field(field, value.value)
return value
class ModelFormatterIter(BaseModelFormatterIter):
"""
Iterate over model fields formatting them.
"""
def format_field(self, field, value):
if isinstance(value, (date, datetime, time)) and not isinstance(field, MultiTypeField):
try:
return field.get_formatted_value(value)
except AttributeError:
return str(value)
elif isinstance(value, timedelta):
return value.total_seconds()
return super(ModelFormatterIter, self).format_field(field, value)
[docs]class JSONEncoder(BaseJSONEncoder):
"""
Json encoder for Dirty Models
"""
default_model_iter = ModelFormatterIter
[docs] def default(self, obj):
if isinstance(obj, BaseModel):
return {k: v for k, v in self.default_model_iter(obj)}
elif isinstance(obj, (BaseModelFormatterIter)):
return {k: v for k, v in obj}
elif isinstance(obj, ListFormatterIter):
return list(obj)
[docs]class Factory:
"""
Factory decorator could be used to define result of a function as default value. It could
be useful to define a :class:`~dirty_models.fields.DateTimeField` with :meth:`datetime.datetime.now`
in order to set the current datetime.
"""
def __init__(self, func):
self.func = func
def __call__(self):
return self.func()
factory = Factory