Skip to content

Commit

Permalink
Merge pull request #108 from alfred82santa/release/0.11.2
Browse files Browse the repository at this point in the history
Fix bug #107; Added ModelIterator utility; Some fixes
  • Loading branch information
alfred82santa committed Feb 20, 2018
2 parents d2dd065 + 3cabc50 commit 354becd
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 30 deletions.
20 changes: 20 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,26 @@ Features
Changelog
---------


Version 0.11.2
--------------

- Fix bug #107.

- Added :class:`~dirty_models.utils.ModelIterator` class in order to be able to iterate over model fields.

.. code-block:: python
from dirty_models.utils import ModelIterator
for fieldname, field_obj, value in ModelIterator(my_model):
print('Field name: {}'.format(fieldname))
print('Field alias: {}'.format(field_obj.alias))
print('Field value: {}'.format(value))
- Some fixes about read only data.


Version 0.11.1
--------------

Expand Down
2 changes: 1 addition & 1 deletion dirty_models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
from .fields import *
from .utils import *

__version__ = '0.11.1'
__version__ = '0.11.2'
8 changes: 3 additions & 5 deletions dirty_models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ class BaseData:
Base class for data inside dirty model.
"""

__slots__ = []

__locked__ = None
__read_only__ = None
__parent__ = None
Expand Down Expand Up @@ -86,15 +84,15 @@ def _prepare_child(self, value):

class InnerFieldTypeMixin:

_field_type = None
__field_type__ = None

def __init__(self, *args, **kwargs):
if 'field_type' in kwargs:
self._field_type = kwargs.pop('field_type')
self.__field_type__ = kwargs.pop('field_type')
super(InnerFieldTypeMixin, self).__init__(*args, **kwargs)

def get_field_type(self):
return self._field_type
return self.__field_type__


class Unlocker():
Expand Down
3 changes: 1 addition & 2 deletions dirty_models/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,8 +456,7 @@ def can_use_value(self, value):

def set_value(self, obj, value: time):
if self.default_timezone and value.tzinfo is None:
value = time(hour=value.hour, minute=value.minute, second=value.microsecond,
microsecond=value.microsecond, tzinfo=self.default_timezone)
value = value.replace(tzinfo=self.default_timezone)

super(TimeField, self).set_value(obj, value)

Expand Down
10 changes: 5 additions & 5 deletions dirty_models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ class BaseDynamicModel(BaseModel):
"""
"""
_dynamic_model = None
__dynamic_model__ = None

def __getattr__(self, name):
try:
Expand Down Expand Up @@ -746,7 +746,7 @@ def _get_field_type(self, key, value):
elif isinstance(value, Enum):
return EnumField(name=key, enum_class=type(value))
elif isinstance(value, (dict, BaseDynamicModel, Mapping)):
return ModelField(name=key, model_class=self._dynamic_model or self.__class__)
return ModelField(name=key, model_class=self.__dynamic_model__ or self.__class__)
elif isinstance(value, BaseModel):
return ModelField(name=key, model_class=value.__class__)
elif isinstance(value, (list, set, ListModel)):
Expand Down Expand Up @@ -802,7 +802,7 @@ def __init__(self, *args, **kwargs):
self.__structure__ = {}

def __new__(cls, *args, **kwargs):
new_class = type('DynamicModel_' + str(cls._next_id), (cls,), {'_dynamic_model': DynamicModel})
new_class = type('DynamicModel_' + str(cls._next_id), (cls,), {'__dynamic_model__': DynamicModel})
cls._next_id = id(new_class)
return super(DynamicModel, new_class).__new__(new_class)

Expand Down Expand Up @@ -880,7 +880,7 @@ def get_real_name(self, name):
return new_name if new_name else name

def get_field_obj(self, name):
return super(HashMapModel, self).get_field_obj(name) or self._field_type
return super(HashMapModel, self).get_field_obj(name) or self.__field_type__

def copy(self):
"""
Expand Down Expand Up @@ -975,7 +975,7 @@ class FastDynamicModel(BaseDynamicModel):

def __init__(self, *args, **kwargs):
self.__field_types__ = {}
self._dynamic_model = FastDynamicModel
self.__dynamic_model__ = FastDynamicModel
super(FastDynamicModel, self).__init__(*args, **kwargs)

def get_real_name(self, name):
Expand Down
46 changes: 36 additions & 10 deletions dirty_models/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from .model_types import ListModel
from .models import BaseModel


__all__ = ['factory', 'JSONEncoder', 'Factory']


Expand All @@ -19,6 +18,39 @@ def underscore_to_camel(string):
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

Expand All @@ -38,21 +70,15 @@ def __iter__(self):
yield self.parent_formatter.format_field(self.field, item)


class BaseModelFormatterIter(BaseFormatterIter):
class BaseModelFormatterIter(BaseModelIterator, BaseFormatterIter):
"""
Base formatter iterator for Dirty Models.
"""

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)
for name, field, value in super(BaseModelFormatterIter, self).__iter__():
yield name, self.format_field(field,
self.model.get_field_value(fieldname))
self.model.get_field_value(name))

def format_field(self, field, value):
if isinstance(field, MultiTypeField):
Expand Down
119 changes: 112 additions & 7 deletions tests/dirty_models/tests_utils.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from datetime import datetime, date, timedelta
from enum import Enum
from datetime import date, datetime, timedelta
from json import dumps, loads
from unittest.case import TestCase

from dirty_models.fields import StringIdField, IntegerField, DateTimeField, ArrayField, MultiTypeField, ModelField, \
HashMapField, DateField, TimedeltaField, EnumField
from enum import Enum

from dirty_models.fields import ArrayField, DateField, DateTimeField, EnumField, HashMapField, IntegerField, \
ModelField, MultiTypeField, StringIdField, TimedeltaField
from dirty_models.models import BaseModel, DynamicModel, FastDynamicModel
from dirty_models.utils import underscore_to_camel, ModelFormatterIter, ListFormatterIter, JSONEncoder
from dirty_models.utils import JSONEncoder, ListFormatterIter, ModelFormatterIter, ModelIterator, underscore_to_camel


class UnderscoreToCamelTests(TestCase):
Expand All @@ -28,7 +29,6 @@ def test_underscore_multi_number(self):


class TestModel(BaseModel):

class TestEnum(Enum):
value_1 = 1
value_2 = '2'
Expand All @@ -41,7 +41,7 @@ class TestEnum(Enum):
test_array_multitype = ArrayField(field_type=MultiTypeField(field_types=[IntegerField(),
DateTimeField(
parse_format="%Y-%m-%dT%H:%M:%S"
)]))
)]))
test_model_field_1 = ArrayField(field_type=ArrayField(field_type=ModelField()))
test_hash_map = HashMapField(field_type=DateField(parse_format="%Y-%m-%d date"))
test_timedelta = TimedeltaField()
Expand Down Expand Up @@ -195,3 +195,108 @@ def test_general_use_json(self):
data = {'foo': 3, 'bar': 'str'}
json_str = dumps(data, cls=JSONEncoder)
self.assertEqual(loads(json_str), data)


class ModelIteratorTests(TestCase):

def test_model_iterator(self):
data = {'other_field': 'foo',
'test_int_field_1': 4,
'test_datetime': datetime(year=2016, month=5, day=30,
hour=22, minute=22, second=22),
'test_array_datetime': [datetime(year=2015, month=5, day=30,
hour=22, minute=22, second=22),
datetime(year=2015, month=6, day=30,
hour=22, minute=22, second=22)],
'test_array_multitype': [datetime(year=2015, month=5, day=30,
hour=22, minute=22, second=22),
4, 5],
'test_model_field_1': [[{'test_datetime': datetime(year=2015, month=7, day=30,
hour=22, minute=22, second=22)}]],
'test_hash_map': {'foo': date(year=2015, month=7, day=30)},
'test_timedelta': timedelta(seconds=32.1122),
'test_enum': TestModel.TestEnum.value_3,
'test_multi_field': date(year=2015, month=7, day=30)}

model = TestModel(data=data)

result = {k: v for k, v in ModelIterator(model)}

self.assertEqual(set(result.keys()), set(data.keys()))
self.assertIsInstance(result['test_hash_map'], BaseModel)

def test_model_iterator_items(self):
data = {'other_field': 'foo',
'test_int_field_1': 4,
'test_datetime': datetime(year=2016, month=5, day=30,
hour=22, minute=22, second=22),
'test_array_datetime': [datetime(year=2015, month=5, day=30,
hour=22, minute=22, second=22),
datetime(year=2015, month=6, day=30,
hour=22, minute=22, second=22)],
'test_array_multitype': [datetime(year=2015, month=5, day=30,
hour=22, minute=22, second=22),
4, 5],
'test_model_field_1': [[{'test_datetime': datetime(year=2015, month=7, day=30,
hour=22, minute=22, second=22)}]],
'test_hash_map': {'foo': date(year=2015, month=7, day=30)},
'test_timedelta': timedelta(seconds=32.1122),
'test_enum': TestModel.TestEnum.value_3,
'test_multi_field': date(year=2015, month=7, day=30)}

model = TestModel(data=data)

result = {k: v for k, v in ModelIterator(model).items()}

self.assertEqual(set(result.keys()), set(data.keys()))
self.assertIsInstance(result['test_hash_map'], BaseModel)

def test_model_iterator_keys(self):
data = {'other_field': 'foo',
'test_int_field_1': 4,
'test_datetime': datetime(year=2016, month=5, day=30,
hour=22, minute=22, second=22),
'test_array_datetime': [datetime(year=2015, month=5, day=30,
hour=22, minute=22, second=22),
datetime(year=2015, month=6, day=30,
hour=22, minute=22, second=22)],
'test_array_multitype': [datetime(year=2015, month=5, day=30,
hour=22, minute=22, second=22),
4, 5],
'test_model_field_1': [[{'test_datetime': datetime(year=2015, month=7, day=30,
hour=22, minute=22, second=22)}]],
'test_hash_map': {'foo': date(year=2015, month=7, day=30)},
'test_timedelta': timedelta(seconds=32.1122),
'test_enum': TestModel.TestEnum.value_3,
'test_multi_field': date(year=2015, month=7, day=30)}

model = TestModel(data=data)

result = [v for v in ModelIterator(model).keys()]

self.assertEqual(set(data.keys()), set(result))

def test_model_iterator_values(self):
data = {'other_field': 'foo',
'test_int_field_1': 4,
'test_datetime': datetime(year=2016, month=5, day=30,
hour=22, minute=22, second=22),
'test_array_datetime': [datetime(year=2015, month=5, day=30,
hour=22, minute=22, second=22),
datetime(year=2015, month=6, day=30,
hour=22, minute=22, second=22)],
'test_array_multitype': [datetime(year=2015, month=5, day=30,
hour=22, minute=22, second=22),
4, 5],
'test_model_field_1': [[{'test_datetime': datetime(year=2015, month=7, day=30,
hour=22, minute=22, second=22)}]],
'test_hash_map': {'foo': date(year=2015, month=7, day=30)},
'test_timedelta': timedelta(seconds=32.1122),
'test_enum': TestModel.TestEnum.value_3,
'test_multi_field': date(year=2015, month=7, day=30)}

model = TestModel(data=data)

result = [v for v in ModelIterator(model).values()]

self.assertIn(model.test_hash_map, result)

0 comments on commit 354becd

Please sign in to comment.