Skip to content

Commit bc3c64e

Browse files
committed
Issue #25: Implementation of contextual view with tests coverage
1 parent ff7a5a1 commit bc3c64e

File tree

3 files changed

+234
-4
lines changed

3 files changed

+234
-4
lines changed

domain_models/models.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ def get(self, field_name, default=None):
192192
converted to right type value.
193193
If the field does not exist, `AttributeError` is raised as well.
194194
195-
:param string field_name:
196-
:param object default:
195+
:param field_name: string
196+
:param default: object
197197
"""
198198
try:
199199
field = self.__class__.__fields__[field_name]
@@ -206,7 +206,7 @@ def get(self, field_name, default=None):
206206
def get_data(self):
207207
"""Read only dictionary of model fields/values.
208208
209-
:rtype dict:
209+
:rtype: dict
210210
"""
211211
return dict((name, field.get_builtin_type(self))
212212
for name, field in
@@ -215,7 +215,7 @@ def get_data(self):
215215
def set_data(self, data):
216216
"""Set dictionary data to model.
217217
218-
:param dict data:
218+
:param data: dict
219219
"""
220220
for name, field in six.iteritems(self.__class__.__fields__):
221221
field.init_model(self, data.get(name))

domain_models/views.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Contextual view module."""
2+
from domain_models import models
3+
4+
5+
class ContextView(object):
6+
"""Contextual view class."""
7+
8+
__model__ = None
9+
10+
def __init__(self, model):
11+
"""Model validation.
12+
13+
:param model: DomainModel
14+
"""
15+
if not issubclass(self.__model__, models.DomainModel):
16+
raise TypeError("Attribute __model__ must be subclass of "
17+
"DomainModel")
18+
19+
if not isinstance(model, self.__model__):
20+
raise TypeError("\"{0}\" is not an instance of {1}".format(
21+
model, self.__model__))
22+
23+
self.__model__ = model
24+
25+
def get_data(self):
26+
"""Read only dictionary fields/values of model within current context.
27+
28+
:rtype: dict
29+
"""
30+
raise NotImplementedError("Method get_data is required.")

tests/test_context_view.py

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
"""ContextViews tests."""
2+
3+
import datetime
4+
5+
import unittest2 as unittest
6+
7+
from domain_models import models
8+
from domain_models import fields
9+
from domain_models import views
10+
11+
12+
class Photo(models.DomainModel):
13+
"""Photo DomainModel to be attached to profile."""
14+
id = fields.Int()
15+
title = fields.String()
16+
path = fields.String()
17+
public = fields.Bool(default=False)
18+
19+
20+
class Profile(models.DomainModel):
21+
"""Profile DomainModel to be tested."""
22+
id = fields.Int()
23+
name = fields.String()
24+
birth_date = fields.Date()
25+
business_address = fields.String()
26+
home_address = fields.String()
27+
main_photo = fields.Model(Photo)
28+
photos = fields.Collection(Photo)
29+
30+
31+
class OtherDomain(models.DomainModel):
32+
"""Other DomainModel for testing of validation."""
33+
id = fields.Int()
34+
35+
36+
class PublicProfile(views.ContextView):
37+
"""Profile data in public context."""
38+
__model__ = Profile
39+
40+
def _get_oid(self):
41+
"""Calculate open id.
42+
43+
:rtype: int
44+
"""
45+
return self.__model__.id << 8
46+
47+
def _get_photos(self):
48+
photos = []
49+
for photo in self.__model__.photos:
50+
photo_data = PublicPhoto(photo).get_data()
51+
if photo_data:
52+
photos.append(photo_data)
53+
return photos
54+
55+
def get_data(self):
56+
main_photo = PublicPhoto(self.__model__.main_photo)
57+
return {
58+
'oid': self._get_oid(),
59+
'name': self.__model__.name,
60+
'business_address': self.__model__.business_address,
61+
'main_photo': main_photo.get_data(),
62+
'photos': self._get_photos()
63+
}
64+
65+
66+
class PrivateProfile(views.ContextView):
67+
"""Profile data in private context."""
68+
__model__ = Profile
69+
70+
def _get_photos(self):
71+
photos = []
72+
for photo in self.__model__.photos:
73+
photo_data = PrivatePhoto(photo).get_data()
74+
if photo_data:
75+
photos.append(photo_data)
76+
return photos
77+
78+
def get_data(self):
79+
main_photo = PrivatePhoto(self.__model__.main_photo)
80+
return {
81+
'id': self.__model__.id,
82+
'name': self.__model__.name,
83+
'birth_date': self.__model__.birth_date,
84+
'business_address': self.__model__.business_address,
85+
'home_address': self.__model__.home_address,
86+
'main_photo': main_photo.get_data(),
87+
'photos': self._get_photos()
88+
}
89+
90+
91+
class PublicPhoto(views.ContextView):
92+
"""Photo data in public context."""
93+
__model__ = Photo
94+
95+
def _get_oid(self):
96+
return self.__model__.id << 8
97+
98+
def get_data(self):
99+
if not self.__model__.public:
100+
return {}
101+
return {
102+
'oid': self._get_oid(),
103+
'title': self.__model__.title,
104+
'path': self.__model__.path
105+
}
106+
107+
108+
class PrivatePhoto(views.ContextView):
109+
__model__ = Photo
110+
111+
def get_data(self):
112+
return {
113+
'id': self.__model__.id,
114+
'title': self.__model__.title,
115+
'path': self.__model__.path
116+
}
117+
118+
119+
class EmptyModelContext(views.ContextView):
120+
"""Badly initiated context."""
121+
pass
122+
123+
124+
class WrongModelContext(views.ContextView):
125+
"""Badly initiated context."""
126+
__model__ = type
127+
128+
129+
class TestContextView(unittest.TestCase):
130+
main_photo = Photo(id=1, title='main photo', path='path/to/the/main/photo',
131+
public=True)
132+
photo2 = Photo(id=2, title='photo 2', path='path/to/the/photo2',
133+
public=False)
134+
photo3 = Photo(id=3, title='photo 3', path='path/to/the/photo3',
135+
public=True)
136+
profile = Profile(id=1,
137+
name='John',
138+
birth_date=datetime.date(1950, 4, 18),
139+
business_address='John works here',
140+
home_address='John lives here',
141+
main_photo=main_photo,
142+
photos=[main_photo, photo2, photo3])
143+
144+
def test_wrong_class_defined(self):
145+
with self.assertRaises(TypeError):
146+
EmptyModelContext("it doesn't matter")
147+
148+
def test_wrong_model_passed(self):
149+
with self.assertRaises(TypeError):
150+
PublicProfile("invalid argument")
151+
152+
def test_context_view(self):
153+
public_context = PublicProfile(self.profile)
154+
private_context = PrivateProfile(self.profile)
155+
156+
self.assertDictEqual(public_context.get_data(), {
157+
'oid': 256,
158+
'name': 'John',
159+
'business_address': 'John works here',
160+
'main_photo': {
161+
'oid': 256,
162+
'title': 'main photo',
163+
'path': 'path/to/the/main/photo'
164+
},
165+
'photos': [{
166+
'oid': 256,
167+
'title': 'main photo',
168+
'path': 'path/to/the/main/photo'
169+
}, {
170+
'oid': 768,
171+
'title': 'photo 3',
172+
'path': 'path/to/the/photo3'
173+
}]
174+
})
175+
176+
self.assertDictEqual(private_context.get_data(), {
177+
'id': 1,
178+
'name': 'John',
179+
'birth_date': datetime.date(1950, 4, 18),
180+
'business_address': 'John works here',
181+
'home_address': 'John lives here',
182+
'main_photo': {
183+
'id': 1,
184+
'title': 'main photo',
185+
'path': 'path/to/the/main/photo'
186+
},
187+
'photos': [{
188+
'id': 1,
189+
'title': 'main photo',
190+
'path': 'path/to/the/main/photo'
191+
}, {
192+
'id': 2,
193+
'title': 'photo 2',
194+
'path': 'path/to/the/photo2'
195+
}, {
196+
'id': 3,
197+
'title': 'photo 3',
198+
'path': 'path/to/the/photo3'
199+
}]
200+
})

0 commit comments

Comments
 (0)