Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ Domain models

Domain models framework for Python projects

============

.. image:: https://travis-ci.org/ets-labs/domain_models.svg?branch=master
:target: https://travis-ci.org/ets-labs/domain_models
:alt: Build Status

.. image:: https://coveralls.io/repos/github/ets-labs/domain_models/badge.svg?branch=master
:target: https://coveralls.io/github/ets-labs/domain_models?branch=master
:alt: Coverage Status

Introduction
~~~~~~~~~~~~

Expand Down
20 changes: 14 additions & 6 deletions domain_models/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
class Field(property):
"""Base field."""

def __init__(self, default=None):
def __init__(self, default=None, required=False):
"""Initializer."""
super(Field, self).__init__(self.get_value, self.set_value)
self.name = None
Expand All @@ -19,6 +19,7 @@ def __init__(self, default=None):
self.model_cls = None

self.default = default
self.required = required

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

def init_model(self, model, value):
"""Init model with field."""
if not value:
if value is None:
value = self.default() if callable(self.default) else self.default

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

setattr(model, self.storage_name, value)

def get_value(self, model):
Expand All @@ -50,6 +55,9 @@ def get_value(self, model):

def set_value(self, model, value):
"""Set field's value."""
if value is None and self.required:
raise AttributeError("This field is required.")

if value is not None:
value = self._converter(value)
setattr(model, self.storage_name, value)
Expand Down Expand Up @@ -122,9 +130,9 @@ def _converter(self, value):
class Model(Field):
"""Model relation field."""

def __init__(self, related_model_cls, default=None):
def __init__(self, related_model_cls, default=None, required=False):
"""Initializer."""
super(Model, self).__init__(default=default)
super(Model, self).__init__(default=default, required=required)

self.related_model_cls = related_model_cls

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

def __init__(self, related_model_cls, default=None):
def __init__(self, related_model_cls, default=None, required=False):
"""Initializer."""
super(Collection, self).__init__(default=default)
super(Collection, self).__init__(default=default, required=required)
self.related_model_cls = related_model_cls

def _converter(self, value):
Expand Down
37 changes: 37 additions & 0 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class ExampleModel(models.DomainModel):
field = fields.Field()
field_default = fields.Field(default=123)
field_default_callable = fields.Field(default=time.time)
field_required_default = fields.Field(default=123, required=True)

bool_field = fields.Bool()

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


class RequiredFieldModel(models.DomainModel):
"""Example model for required fields."""
field_required = fields.Field(required=True)


class FieldTest(unittest.TestCase):
"""Base field tests."""

Expand Down Expand Up @@ -81,6 +87,37 @@ def test_field_default_callable(self):
self.assertGreater(model2.field_default_callable,
model1.field_default_callable)

def test_field_required(self):
"""Test required field with default value."""
model = ExampleModel()
self.assertEquals(model.field_required_default, 123)

def test_field_required_set_valid(self):
"""Test required field with valid value."""
model = ExampleModel()
model.field_required_default = False
self.assertIs(model.field_required_default, False)

def test_field_required_set_invalid(self):
"""Test required field with invalid value."""
model = ExampleModel()
with self.assertRaises(AttributeError):
model.field_required_default = None

def test_field_required_init_valid_model(self):
"""Test required field with valid value as model keyword."""
model = RequiredFieldModel(field_required=False)
self.assertIs(model.field_required, False)
model.field_required = 123
self.assertEqual(model.field_required, 123)

def test_field_required_init_invalid_model(self):
"""Test required field with invalid value as model keyword."""
with self.assertRaises(AttributeError):
RequiredFieldModel()
with self.assertRaises(AttributeError):
RequiredFieldModel(field_required=None)


class BoolTest(unittest.TestCase):
"""Bool field tests."""
Expand Down