Skip to content

Commit

Permalink
Add default values & multi type field
Browse files Browse the repository at this point in the history
  • Loading branch information
alfred82santa committed Mar 15, 2016
1 parent 79d7676 commit 1cb28de
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 4 deletions.
45 changes: 44 additions & 1 deletion dirty_models/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ class BaseField:

"""Base field descriptor."""

def __init__(self, name=None, alias=None, getter=None, setter=None, read_only=False, doc=None):
def __init__(self, name=None, alias=None, getter=None, setter=None, read_only=False, default=None, doc=None):
self._name = None
self.name = name
self.alias = alias
self.read_only = read_only
self.default = default
self._getter = getter
self._setter = setter
self.__doc__ = doc or self.get_field_docstring()
Expand Down Expand Up @@ -482,3 +483,45 @@ def convert_value(self, value):

class BlobField(BaseField):
pass


class MultiTypeField(BaseField):

def __init__(self, field_types=None, **kwargs):
self._field_types = []

field_types = field_types or []

for field_type in field_types:
if isinstance(field_type, tuple):
field_type = field_type[0](**field_type[1])
self._field_types.append(field_type if field_type else BaseField())
super(MultiTypeField, self).__init__(**kwargs)

def get_field_docstring(self):
if len(self._field_types):
return 'Multiple type values allowed:\n{0}'.format("\n".join(["* {0}".format(field.get_field_docstring())
for field in self._field_types]))

def export_definition(self):
result = super(MultiTypeField, self).export_definition()
result['field_types'] = [(field_type.__class__, field_type.export_definition())
for field_type in self._field_types]
return result

def convert_value(self, value):
for ft in self._field_types:
if ft.can_use_value(value):
return ft.convert_value(value)

def check_value(self, value):
for ft in self._field_types:
if ft.check_value(value):
return True
return False

def can_use_value(self, value):
for ft in self._field_types:
if ft.can_use_value(value):
return True
return False
14 changes: 14 additions & 0 deletions dirty_models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from datetime import datetime

from collections import Mapping
from copy import deepcopy

from dirty_models.base import BaseData, InnerFieldTypeMixin
from dirty_models.fields import IntegerField, FloatField, BooleanField, StringField, DateTimeField
Expand Down Expand Up @@ -35,6 +36,16 @@ def __init__(cls, name, bases, classdict):
read_only_fields.append(field.name)

cls._structure = structure
default_data = {}
for p in bases:
try:
default_data.update(deepcopy(p._default_data))
except AttributeError:
pass

default_data.update(deepcopy(cls._default_data))
default_data.update({f.name: f.default for f in structure.values() if f.default is not None})
cls._default_data = default_data

def process_base_field(cls, field, key):
"""
Expand Down Expand Up @@ -83,13 +94,16 @@ class BaseModel(BaseData, metaclass=DirtyModelMeta):
_modified_data = None
_deleted_fields = None

_default_data = {}

def __init__(self, data=None, flat=False, *args, **kwargs):
super(BaseModel, self).__init__(*args, **kwargs)
self._original_data = {}
self._modified_data = {}
self._deleted_fields = []

self.unlock()
self.import_data(self._default_data)
if isinstance(data, (dict, Mapping)):
self.import_data(data)
self.import_data(kwargs)
Expand Down
97 changes: 96 additions & 1 deletion tests/dirty_models/tests_fields.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from unittest import TestCase
from dirty_models.fields import (IntegerField, StringField, BooleanField,
FloatField, ModelField, TimeField, DateField,
DateTimeField, ArrayField, StringIdField, HashMapField)
DateTimeField, ArrayField, StringIdField, HashMapField, MultiTypeField)
from dirty_models.models import BaseModel, HashMapModel
from dirty_models.model_types import ListModel

Expand Down Expand Up @@ -1309,3 +1309,98 @@ def test_array_field_no_autolist(self):
self.model.__class__.__dict__['array_field'].autolist = False
self.model.array_field = 'foo'
self.assertEqual(self.model.export_data(), {})


class TestMultiTypeFieldSimpleTypes(TestCase):

def setUp(self):
super(TestMultiTypeFieldSimpleTypes, self).setUp()

class MultiTypeModel(BaseModel):
multi_field = MultiTypeField(field_types=[IntegerField(), StringField()])

self.model = MultiTypeModel()

def test_string_field(self):
self.model.multi_field = 'foo'
self.assertEqual(self.model.multi_field, 'foo')

def test_integer_field(self):
self.model.multi_field = 3
self.assertEqual(self.model.multi_field, 3)

def test_update_string_field(self):
self.model.multi_field = 3
self.model.flat_data()
self.model.multi_field = 'foo'
self.assertEqual(self.model.multi_field, 'foo')

def test_update_integer_field(self):
self.model.multi_field = 'foo'
self.model.flat_data()
self.model.multi_field = 3
self.assertEqual(self.model.multi_field, 3)

def test_no_update_integer_field(self):
self.model.multi_field = 3
self.model.flat_data()
self.model.multi_field = [3, 4]
self.assertEqual(self.model.multi_field, 3)

def test_integer_field_use_float(self):
self.model.multi_field = 3.0
self.assertEqual(self.model.multi_field, 3)

def test_string_field_conversion_priority(self):
self.model.multi_field = '3'
self.assertEqual(self.model.multi_field, '3')

def test_multi_field_desc(self):
self.maxDiff = None
field = MultiTypeField(field_types=[IntegerField(), StringField()])
self.assertEqual(field.export_definition(), {
'alias': None,
'doc': "\n".join(['Multiple type values allowed:',
'* IntegerField field',
'* StringField field']),
'field_types': [(IntegerField, {'alias': None,
'doc': 'IntegerField field',
'name': None,
'read_only': False}),
(StringField, {'alias': None,
'doc': 'StringField field',
'name': None,
'read_only': False})],
'name': None,
'read_only': False})


class TestMultiTypeFieldComplexTypes(TestCase):

def setUp(self):
super(TestMultiTypeFieldComplexTypes, self).setUp()

class MultiTypeModel(BaseModel):
multi_field = MultiTypeField(field_types=[IntegerField(), (ArrayField, {"field_type": StringField()})])

self.model = MultiTypeModel()

def test_integer_field(self):
self.model.multi_field = 3
self.assertEqual(self.model.multi_field, 3)

def test_array_field(self):
self.model.multi_field = ['foo', 'bar']
self.assertEqual(self.model.multi_field.export_data(), ['foo', 'bar'])

def test_update_array_field(self):
self.model.multi_field = 3
self.model.flat_data()
self.model.multi_field = ['foo', 'bar']
self.assertEqual(self.model.multi_field.export_data(), ['foo', 'bar'])

def test_update_integer_field(self):
self.model.multi_field = ['foo', 'bar']
self.model.flat_data()
self.model.multi_field = 3
self.assertEqual(self.model.multi_field, 3)
147 changes: 145 additions & 2 deletions tests/dirty_models/tests_models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import pickle
from datetime import datetime
from datetime import datetime, date, time
from unittest import TestCase

from dirty_models.base import Unlocker
from dirty_models.fields import (BaseField, IntegerField, FloatField,
StringField, DateTimeField, ModelField,
ArrayField, BooleanField)
ArrayField, BooleanField, DateField, TimeField, HashMapField)
from dirty_models.models import BaseModel, DynamicModel, HashMapModel, FastDynamicModel

INITIAL_DATA = {
Expand Down Expand Up @@ -1243,3 +1243,146 @@ def test_no_type_def(self):
model = PickableHashMapModel()
model.field1 = 'sdsd'
self.assertEqual(model.field1, 'sdsd')


class SecondaryModel(BaseModel):

field_integer = IntegerField(default=2)
field_string = StringField(default='test')


class ModelDefaultValues(BaseModel):

field_integer = IntegerField(default=1)
field_string = StringField(default='foobar')
field_boolean = BooleanField(default=True)
field_float = FloatField(default=0.1)
field_date = DateField(default=date(2016, 11, 23))
field_time = TimeField(default=time(23, 56, 59))
field_datetime = DateTimeField(default=datetime(2016, 11, 23, 23, 56, 59))
field_array_integer = ArrayField(field_type=IntegerField(), default=[1, 3, 4])
field_model = ModelField(model_class=SecondaryModel, default={"field_integer": 5})
field_hashmap = HashMapField(field_type=StringField(), default={"item1": "aaaa",
"item2": "bbbb"})


class TestDefaultValues(TestCase):

def test_field_default_value(self):

model = ModelDefaultValues()

self.assertEqual(model.field_integer, 1)
self.assertEqual(model.field_string, 'foobar')
self.assertEqual(model.field_boolean, True)
self.assertEqual(model.field_float, 0.1)
self.assertEqual(model.field_date, date(2016, 11, 23))
self.assertEqual(model.field_time, time(23, 56, 59))
self.assertEqual(model.field_datetime, datetime(2016, 11, 23, 23, 56, 59))
self.assertEqual(model.field_array_integer.export_data(), [1, 3, 4])
self.assertEqual(model.field_model.export_data(), {"field_integer": 5,
"field_string": "test"})
self.assertEqual(model.field_hashmap.export_data(), {"item1": "aaaa",
"item2": "bbbb"})

def test_original_data(self):
model = ModelDefaultValues()
self.assertEqual(model.export_original_data(), {})

def test_modified_data(self):
model = ModelDefaultValues()
self.assertEqual(model.export_modified_data(), {'field_array_integer': [1, 3, 4],
'field_boolean': True,
'field_date': date(2016, 11, 23),
'field_datetime': datetime(2016, 11, 23, 23, 56, 59),
'field_float': 0.1,
'field_hashmap': {'item1': 'aaaa', 'item2': 'bbbb'},
'field_integer': 1,
'field_model': {'field_integer': 5, 'field_string': 'test'},
'field_string': 'foobar',
'field_time': time(23, 56, 59)})

def test_modify_model(self):
model = ModelDefaultValues()
del model.field_model
del model.field_hashmap

model.field_integer = 4
model.field_string = 'test'

self.assertEqual(model.export_modified_data(), {'field_array_integer': [1, 3, 4],
'field_boolean': True,
'field_date': date(2016, 11, 23),
'field_datetime': datetime(2016, 11, 23, 23, 56, 59),
'field_float': 0.1,
'field_integer': 4,
'field_string': 'test',
'field_time': time(23, 56, 59)})


class ModelGeneralDefault(ModelDefaultValues):

_default_data = {'field_array_integer': [20, 30, 40],
'field_boolean': False,
'field_datetime': datetime(2017, 11, 23, 23, 56, 59),
'field_float': 1.1,
'field_hashmap': {'item3': 'cccc', 'item4': 'dddd'},
'field_integer': 9,
'field_model': {'field_integer': 6, 'field_string': 'tost'},
'field_string': 'barfoo',
'field_time': time(13, 56, 59)}


class TestGeneralDefaultValues(TestCase):

def test_field_default_value(self):

model = ModelGeneralDefault()

self.assertEqual(model.field_integer, 9)
self.assertEqual(model.field_string, 'barfoo')
self.assertEqual(model.field_boolean, False)
self.assertEqual(model.field_float, 1.1)
self.assertEqual(model.field_date, date(2016, 11, 23))
self.assertEqual(model.field_time, time(13, 56, 59))
self.assertEqual(model.field_datetime, datetime(2017, 11, 23, 23, 56, 59))
self.assertEqual(model.field_array_integer.export_data(), [20, 30, 40])
self.assertEqual(model.field_model.export_data(), {"field_integer": 6,
"field_string": "tost"})
self.assertEqual(model.field_hashmap.export_data(), {"item3": "cccc",
"item4": "dddd"})

def test_original_data(self):
model = ModelGeneralDefault()
self.assertEqual(model.export_original_data(), {})

def test_modified_data(self):
model = ModelGeneralDefault()

self.assertEqual(model.export_modified_data(), {'field_array_integer': [20, 30, 40],
'field_boolean': False,
'field_date': date(2016, 11, 23),
'field_datetime': datetime(2017, 11, 23, 23, 56, 59),
'field_float': 1.1,
'field_hashmap': {'item3': 'cccc', 'item4': 'dddd'},
'field_integer': 9,
'field_model': {'field_integer': 6, 'field_string': 'tost'},
'field_string': 'barfoo',
'field_time': time(13, 56, 59)})

def test_modify_model(self):
model = ModelGeneralDefault()
del model.field_model
del model.field_hashmap

model.field_integer = 4
model.field_string = 'test'

self.assertEqual(model.export_modified_data(), {'field_array_integer': [20, 30, 40],
'field_boolean': False,
'field_date': date(2016, 11, 23),
'field_datetime': datetime(2017, 11, 23, 23, 56, 59),
'field_float': 1.1,
'field_integer': 4,
'field_string': 'test',
'field_time': time(13, 56, 59)})

0 comments on commit 1cb28de

Please sign in to comment.