Skip to content

Commit 51e4f8c

Browse files
committed
Merge pull request #18 from ets-labs/16-mark-field-as-required
#16: Possibility to mark filed as required.
2 parents 9ed9d74 + 05d96b3 commit 51e4f8c

File tree

3 files changed

+61
-6
lines changed

3 files changed

+61
-6
lines changed

README.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ Domain models
33

44
Domain models framework for Python projects
55

6+
============
7+
8+
.. image:: https://travis-ci.org/ets-labs/domain_models.svg?branch=master
9+
:target: https://travis-ci.org/ets-labs/domain_models
10+
:alt: Build Status
11+
12+
.. image:: https://coveralls.io/repos/github/ets-labs/domain_models/badge.svg?branch=master
13+
:target: https://coveralls.io/github/ets-labs/domain_models?branch=master
14+
:alt: Coverage Status
15+
616
Introduction
717
~~~~~~~~~~~~
818

domain_models/fields.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
class Field(property):
1111
"""Base field."""
1212

13-
def __init__(self, default=None):
13+
def __init__(self, default=None, required=False):
1414
"""Initializer."""
1515
super(Field, self).__init__(self.get_value, self.set_value)
1616
self.name = None
@@ -19,6 +19,7 @@ def __init__(self, default=None):
1919
self.model_cls = None
2020

2121
self.default = default
22+
self.required = required
2223

2324
def bind_name(self, name):
2425
"""Bind field to its name in model class."""
@@ -40,8 +41,12 @@ def bind_model_cls(self, model_cls):
4041

4142
def init_model(self, model, value):
4243
"""Init model with field."""
43-
if not value:
44+
if value is None:
4445
value = self.default() if callable(self.default) else self.default
46+
47+
if value is None and self.required:
48+
raise AttributeError("This field is required.")
49+
4550
setattr(model, self.storage_name, value)
4651

4752
def get_value(self, model):
@@ -50,6 +55,9 @@ def get_value(self, model):
5055

5156
def set_value(self, model, value):
5257
"""Set field's value."""
58+
if value is None and self.required:
59+
raise AttributeError("This field is required.")
60+
5361
if value is not None:
5462
value = self._converter(value)
5563
setattr(model, self.storage_name, value)
@@ -122,9 +130,9 @@ def _converter(self, value):
122130
class Model(Field):
123131
"""Model relation field."""
124132

125-
def __init__(self, related_model_cls, default=None):
133+
def __init__(self, related_model_cls, default=None, required=False):
126134
"""Initializer."""
127-
super(Model, self).__init__(default=default)
135+
super(Model, self).__init__(default=default, required=required)
128136

129137
self.related_model_cls = related_model_cls
130138

@@ -140,9 +148,9 @@ def _converter(self, value):
140148
class Collection(Field):
141149
"""Models collection relation field."""
142150

143-
def __init__(self, related_model_cls, default=None):
151+
def __init__(self, related_model_cls, default=None, required=False):
144152
"""Initializer."""
145-
super(Collection, self).__init__(default=default)
153+
super(Collection, self).__init__(default=default, required=required)
146154
self.related_model_cls = related_model_cls
147155

148156
def _converter(self, value):

tests/test_fields.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class ExampleModel(models.DomainModel):
2222
field = fields.Field()
2323
field_default = fields.Field(default=123)
2424
field_default_callable = fields.Field(default=time.time)
25+
field_required_default = fields.Field(default=123, required=True)
2526

2627
bool_field = fields.Bool()
2728

@@ -38,6 +39,11 @@ class ExampleModel(models.DomainModel):
3839
collection_field = fields.Collection(RelatedModel)
3940

4041

42+
class RequiredFieldModel(models.DomainModel):
43+
"""Example model for required fields."""
44+
field_required = fields.Field(required=True)
45+
46+
4147
class FieldTest(unittest.TestCase):
4248
"""Base field tests."""
4349

@@ -81,6 +87,37 @@ def test_field_default_callable(self):
8187
self.assertGreater(model2.field_default_callable,
8288
model1.field_default_callable)
8389

90+
def test_field_required(self):
91+
"""Test required field with default value."""
92+
model = ExampleModel()
93+
self.assertEquals(model.field_required_default, 123)
94+
95+
def test_field_required_set_valid(self):
96+
"""Test required field with valid value."""
97+
model = ExampleModel()
98+
model.field_required_default = False
99+
self.assertIs(model.field_required_default, False)
100+
101+
def test_field_required_set_invalid(self):
102+
"""Test required field with invalid value."""
103+
model = ExampleModel()
104+
with self.assertRaises(AttributeError):
105+
model.field_required_default = None
106+
107+
def test_field_required_init_valid_model(self):
108+
"""Test required field with valid value as model keyword."""
109+
model = RequiredFieldModel(field_required=False)
110+
self.assertIs(model.field_required, False)
111+
model.field_required = 123
112+
self.assertEqual(model.field_required, 123)
113+
114+
def test_field_required_init_invalid_model(self):
115+
"""Test required field with invalid value as model keyword."""
116+
with self.assertRaises(AttributeError):
117+
RequiredFieldModel()
118+
with self.assertRaises(AttributeError):
119+
RequiredFieldModel(field_required=None)
120+
84121

85122
class BoolTest(unittest.TestCase):
86123
"""Bool field tests."""

0 commit comments

Comments
 (0)