Skip to content

Commit 2a99328

Browse files
committed
Merge remote-tracking branch 'origin/22-get_method_of_model'
2 parents dd562aa + 4b9089a commit 2a99328

File tree

3 files changed

+212
-16
lines changed

3 files changed

+212
-16
lines changed

domain_models/fields.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,26 @@ def bind_model_cls(self, model_cls):
4141

4242
def init_model(self, model, value):
4343
"""Init model with field."""
44-
if value is None:
44+
if value is None and self.default is not None:
4545
value = self.default() if callable(self.default) else self.default
46+
value = self._converter(value)
4647

4748
if value is None and self.required:
4849
raise AttributeError("This field is required.")
4950

5051
setattr(model, self.storage_name, value)
5152

52-
def get_value(self, model):
53-
"""Return field's value."""
54-
return getattr(model, self.storage_name)
53+
def get_value(self, model, default=None):
54+
"""Return field's value.
55+
56+
:param DomainModel model:
57+
:param mixed default:
58+
"""
59+
if default is not None:
60+
default = self._converter(default)
61+
62+
value = getattr(model, self.storage_name)
63+
return value if value is not None else default
5564

5665
def set_value(self, model, value):
5766
"""Set field's value."""
@@ -60,6 +69,7 @@ def set_value(self, model, value):
6069

6170
if value is not None:
6271
value = self._converter(value)
72+
6373
setattr(model, self.storage_name, value)
6474

6575
def _converter(self, value):

domain_models/models.py

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ def prepare_fields_attribute(attribute_name, attributes, class_name):
6363
@staticmethod
6464
def bind_fields_to_model_cls(cls, model_fields):
6565
"""Bind fields to model class."""
66-
return tuple(field.bind_model_cls(cls)
67-
for field in model_fields)
66+
return dict(
67+
(field.name, field.bind_model_cls(cls)) for field in model_fields)
6868

6969
@staticmethod
7070
def bind_collection_to_model_cls(cls):
@@ -92,9 +92,9 @@ class DomainModel(object):
9292
9393
.. py:attribute:: __fields__
9494
95-
Tuple of all model fields.
95+
Dictionary of all model fields.
9696
97-
:type: tuple[fields.Field]
97+
:type: dict[str, fields.Field]
9898
9999
.. py:attribute:: __unique_key__
100100
@@ -111,15 +111,15 @@ class DomainModel(object):
111111

112112
Collection = collections.Collection
113113

114-
__fields__ = tuple()
114+
__fields__ = dict()
115115
__view_key__ = tuple()
116116
__unique_key__ = tuple()
117117
__slots_optimization__ = True
118118

119119
def __init__(self, **kwargs):
120120
"""Initializer."""
121-
for field in self.__class__.__fields__:
122-
field.init_model(self, kwargs.get(field.name))
121+
for name, field in six.iteritems(self.__class__.__fields__):
122+
field.init_model(self, kwargs.get(name))
123123
super(DomainModel, self).__init__()
124124

125125
def __eq__(self, other):
@@ -164,14 +164,16 @@ def __repr__(self):
164164
"""Return Pythonic representation of domain model."""
165165
return '{module}.{cls}({fields_values})'.format(
166166
module=self.__class__.__module__, cls=self.__class__.__name__,
167-
fields_values=', '.join('='.join((field.name,
168-
repr(field.get_value(self))))
169-
for field in self.__class__.__fields__))
167+
fields_values=', '.join(
168+
'='.join((name, repr(field.get_value(self))))
169+
for name, field in
170+
six.iteritems(self.__class__.__fields__)))
170171

171172
def __str__(self):
172173
"""Return string representation of domain model."""
173174
if not self.__class__.__view_key__:
174175
return self.__repr__()
176+
175177
return '{module}.{cls}({fields_values})'.format(
176178
module=self.__class__.__module__, cls=self.__class__.__name__,
177179
fields_values=', '.join('='.join((field.name,
@@ -181,5 +183,29 @@ def __str__(self):
181183
@property
182184
def __data__(self):
183185
"""Read only dictionary of model fields/values."""
184-
return dict((field.name, field.get_value(self))
185-
for field in self.__class__.__fields__)
186+
return dict((name, field.get_value(self))
187+
for name, field in
188+
six.iteritems(self.__class__.__fields__))
189+
190+
def get(self, field_name, default=None):
191+
"""Return the value of the field.
192+
193+
Analogue for `dict.get()` python method.
194+
`field_name` must be a string. If the string is the name of
195+
one of the existent fields, the result is the value of that field.
196+
For example, `model.get('foobar')` is equivalent to `model.foobar`.
197+
If the filed does not have a value, `default` is returned if provided.
198+
It will raise `TypeError` or `ValueError` if `default` can not be
199+
converted to right type value.
200+
If the field does not exist, `AttributeError` is raised as well.
201+
202+
:param string field_name:
203+
:param mixed default:
204+
"""
205+
try:
206+
field = self.__class__.__fields__[field_name]
207+
except KeyError:
208+
raise AttributeError(
209+
"Field {0} does not exist.".format(field_name))
210+
else:
211+
return field.get_value(self, default)

tests/test_models.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
"""Models tests."""
22

3+
import datetime
4+
35
import unittest2 as unittest
46

7+
import six
8+
59
from domain_models import models
610
from domain_models import fields
711
from domain_models import collections
@@ -164,6 +168,162 @@ class Model2(models.DomainModel):
164168

165169
field = Model1.field
166170

171+
def test_get_method_on_undefined(self):
172+
"""Test method get of Model."""
173+
174+
class Model(models.DomainModel):
175+
"""Test model."""
176+
field = fields.Int()
177+
178+
model = Model()
179+
180+
with self.assertRaises(AttributeError):
181+
model.get('undefined')
182+
183+
def test_get_method_on_int(self):
184+
"""Test method get on Int of Model."""
185+
valid_defaults = [0, 3, 5.5, False, True]
186+
187+
class Model(models.DomainModel):
188+
"""Test model."""
189+
field = fields.Int()
190+
191+
model = Model(field=2)
192+
for value in valid_defaults:
193+
self.assertEqual(model.get('field', default=value), 2)
194+
195+
model = Model()
196+
self.assertEqual(model.get('field'), None)
197+
for value in valid_defaults:
198+
self.assertEqual(model.get('field', default=value), int(value))
199+
200+
for value in ['', u'baz']:
201+
with self.assertRaises(ValueError):
202+
model.get('field', value)
203+
self.fail("Failed with {0}".format(value))
204+
205+
with self.assertRaises(TypeError):
206+
model.get('field', object())
207+
208+
def test_get_method_on_string(self):
209+
"""Test method get on String of Model."""
210+
valid_defaults = ['', 'baz', u'baz', False, True, 1, 2.3]
211+
212+
class Model(models.DomainModel):
213+
"""Test model."""
214+
field = fields.String()
215+
216+
model = Model(field='foobar')
217+
for value in valid_defaults:
218+
self.assertEqual(model.get('field', default=value), 'foobar')
219+
220+
model = Model()
221+
self.assertEqual(model.get('field'), None)
222+
for value in valid_defaults:
223+
self.assertEqual(model.get('field', default=value), str(value))
224+
225+
def test_get_method_on_bool(self):
226+
"""Test method get on Bool of Model."""
227+
228+
class Model(models.DomainModel):
229+
"""Test model."""
230+
field = fields.Bool()
231+
232+
model = Model(field=True)
233+
self.assertEqual(model.get('field'), True)
234+
self.assertEqual(model.get('field', default=False), True)
235+
236+
model = Model(field=False)
237+
self.assertEqual(model.get('field'), False)
238+
self.assertEqual(model.get('field', default=True), False)
239+
240+
model = Model()
241+
self.assertEqual(model.get('field'), None)
242+
self.assertEqual(model.get('field', default=False), False)
243+
self.assertEqual(model.get('field', default=True), True)
244+
245+
def test_get_method_on_float(self):
246+
"""Test method get on Float of Model."""
247+
valid_defaults = [7.5, 7, '7.5', '7', 0, '0.0', .9, '.9', False, True]
248+
249+
class Model(models.DomainModel):
250+
"""Test model."""
251+
field = fields.Float()
252+
253+
model = Model(field=5.5)
254+
self.assertEqual(model.get('field'), 5.5)
255+
for value in valid_defaults:
256+
self.assertEqual(model.get('field', default=value), 5.5)
257+
258+
model = Model()
259+
self.assertEqual(model.get('field'), None)
260+
for value in valid_defaults:
261+
self.assertEqual(model.get('field', default=value), float(value))
262+
263+
for value in ['', 'baz', u'baz']:
264+
with self.assertRaises(ValueError):
265+
model.get('field', value)
266+
self.fail("Failed with {0}".format(value))
267+
268+
with self.assertRaises(TypeError):
269+
model.get('field', object())
270+
271+
def test_get_method_on_date(self):
272+
"""Test method get on Date of Model."""
273+
once = datetime.date(year=1986, month=4, day=26)
274+
today = datetime.date.today()
275+
276+
class Model(models.DomainModel):
277+
"""Test model."""
278+
field = fields.Date()
279+
280+
model = Model(field=today)
281+
self.assertEqual(model.get('field'), today)
282+
self.assertEqual(model.get('field', once), today)
283+
284+
model = Model()
285+
self.assertEqual(model.get('field'), None)
286+
self.assertEqual(model.get('field', once), once)
287+
288+
for value in ['', 'baz', u'baz', 0, 3, 0.7, '.5', False, True]:
289+
with self.assertRaises(TypeError):
290+
model.get('field', value)
291+
self.fail("Failed with {0}".format(value))
292+
293+
def test_get_method_on_datetime(self):
294+
"""Test method get on Date of Model."""
295+
once = datetime.datetime(year=1986, month=4, day=26)
296+
now = datetime.datetime.now()
297+
298+
class Model(models.DomainModel):
299+
"""Test model."""
300+
field = fields.DateTime()
301+
302+
model = Model(field=now)
303+
self.assertEqual(model.get('field'), now)
304+
self.assertEqual(model.get('field', once), now)
305+
306+
model = Model()
307+
self.assertEqual(model.get('field'), None)
308+
self.assertEqual(model.get('field', once), once)
309+
310+
for value in ['', 'baz', u'baz', 0, 3, 0.7, '.5', False, True]:
311+
with self.assertRaises(TypeError):
312+
model.get('field', value)
313+
self.fail("Failed with {0}".format(value))
314+
315+
def test_get_method_on_binary(self):
316+
"""Test method get on Binary of Model."""
317+
self.skipTest("Test is not implemented yet")
318+
319+
def test_get_method_on_model(self):
320+
"""Test method get on Model of Model."""
321+
self.skipTest("Test is not implemented yet")
322+
323+
def test_get_method_on_collection(self):
324+
"""Test method get on Collection of Model."""
325+
self.skipTest("Test is not implemented yet")
326+
167327

168328
class ModelReprTests(unittest.TestCase):
169329
"""Tests for model Pythonic representation."""

0 commit comments

Comments
 (0)