david / django-roa (http://welldev.org/)
Turn your models into remote resources that you can access through Django's ORM. ROA stands for Resource Oriented Architecture.
| commit 114: | 9c6ba819c7b7 |
| parent 113: | e2fb48432d20 |
| branch: | default |
Changed (Δ2.6 KB):
django_roa/db/models.py (109 lines added, 84 lines removed)
examples/django_roa_client/models.py (10 lines added, 0 lines removed)
examples/django_roa_client/tests.py (16 lines added, 1 lines removed)
examples/django_roa_server/urls.py (2 lines added, 0 lines removed)
Up to file-list django_roa/db/models.py:
| … | … | @@ -242,105 +242,130 @@ class ROAModel(models.Model): |
242 |
242 |
def get_resource_url_detail(self): |
243 |
243 |
return u"%s%s/" % (self.get_resource_url_list(), self.pk) |
244 |
244 |
|
245 |
def save_base(self, raw=False, cls=None, force_insert=False, |
|
246 |
force_update=False): |
|
245 |
def save_base(self, raw=False, cls=None, origin=None, |
|
246 |
force_insert=False, force_update=False): |
|
247 |
""" |
|
248 |
Does the heavy-lifting involved in saving. Subclasses shouldn't need to |
|
249 |
override this method. It's separate from save() in order to hide the |
|
250 |
need for overrides of save() to pass around internal-only parameters |
|
251 |
('raw', 'cls', and 'origin'). |
|
252 |
""" |
|
247 |
253 |
assert not (force_insert and force_update) |
248 |
if |
|
254 |
if cls is None: |
|
249 |
255 |
cls = self.__class__ |
250 |
meta = self._meta |
|
251 |
signal = True |
|
252 |
|
|
256 |
meta = cls._meta |
|
257 |
if not meta.proxy: |
|
258 |
origin = cls |
|
253 |
259 |
else: |
254 |
260 |
meta = cls._meta |
255 |
signal = False |
|
256 |
||
261 |
||
262 |
if origin: |
|
263 |
signals.pre_save.send(sender=origin, instance=self, raw=raw) |
|
264 |
||
257 |
265 |
# If we are in a raw save, save the object exactly as presented. |
258 |
266 |
# That means that we don't try to be smart about saving attributes |
259 |
267 |
# that might have come from the parent class - we just save the |
260 |
268 |
# attributes we have been given to the class we have been given. |
261 |
|
|
269 |
# We also go through this process to defer the save of proxy objects |
|
270 |
# to their actual underlying model. |
|
271 |
if not raw or meta.proxy: |
|
272 |
if meta.proxy: |
|
273 |
org = cls |
|
274 |
else: |
|
275 |
org = None |
|
262 |
276 |
for parent, field in meta.parents.items(): |
263 |
277 |
# At this point, parent's primary key field may be unknown |
264 |
278 |
# (for example, from administration form which doesn't fill |
265 |
279 |
# this field). If so, fill it. |
266 |
if |
|
280 |
if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None: |
|
267 |
281 |
setattr(self, parent._meta.pk.attname, getattr(self, field.attname)) |
282 |
||
283 |
self.save_base(cls=parent, origin=org) |
|
284 |
||
285 |
if field: |
|
286 |
setattr(self, field.attname, self._get_pk_val(parent._meta)) |
|
287 |
if meta.proxy: |
|
288 |
return |
|
289 |
||
290 |
if not meta.proxy: |
|
291 |
pk_val = self._get_pk_val(meta) |
|
292 |
pk_set = pk_val is not None |
|
293 |
||
294 |
ROA_FORMAT = getattr(settings, "ROA_FORMAT", 'json') |
|
295 |
get_args = {'format': ROA_FORMAT} |
|
296 |
||
297 |
serializer = serializers.get_serializer(ROA_FORMAT) |
|
298 |
if hasattr(serializer, 'serialize_object'): |
|
299 |
payload = serializer().serialize_object(self) |
|
300 |
else: |
|
301 |
payload = {} |
|
302 |
for field in meta.local_fields: |
|
303 |
||
304 |
# Handle FK fields |
|
305 |
if isinstance(field, models.ForeignKey): |
|
306 |
field_attr = getattr(self, field.name) |
|
307 |
if field_attr is None: |
|
308 |
payload[field.attname] = None |
|
309 |
else: |
|
310 |
payload[field.attname] = field_attr.id |
|
311 |
||
312 |
# Handle all other fields |
|
313 |
else: |
|
314 |
payload[field.name] = field.value_to_string(self) |
|
268 |
315 |
|
269 |
#self.save_base(raw, parent) |
|
270 |
setattr(self, field.attname, self._get_pk_val(parent._meta)) |
|
271 |
||
272 |
pk_val = self._get_pk_val(meta) |
|
273 |
pk_set = pk_val is not None |
|
274 |
||
275 |
ROA_FORMAT = getattr(settings, "ROA_FORMAT", 'json') |
|
276 |
get_args = {'format': ROA_FORMAT} |
|
316 |
# Handle M2M relations in case of update |
|
317 |
if force_update or pk_set and not self.id is None: |
|
318 |
for field in meta.many_to_many: |
|
319 |
# First try to get ids from var set in query's add/remove/clear |
|
320 |
if hasattr(self, '%s_updated_ids' % field.attname): |
|
321 |
field_ids = getattr(self, '%s_updated_ids' % field.attname) |
|
322 |
else: |
|
323 |
field_ids = [obj.id for obj in field.value_from_object(self)] |
|
324 |
payload[field.attname] = ','.join(smart_unicode(id) for id in field_ids) |
|
325 |
||
326 |
if force_update or pk_set and not self.id is None: |
|
327 |
record_exists = True |
|
328 |
resource = Resource(self.get_resource_url_detail()) |
|
329 |
try: |
|
330 |
logger.debug(u"""Modifying : "%s" through %s |
|
331 |
with payload "%s" and GET args "%s" """ % ( |
|
332 |
force_unicode(self), |
|
333 |
force_unicode(resource.uri), |
|
334 |
force_unicode(payload), |
|
335 |
force_unicode(get_args))) |
|
336 |
response = resource.put(payload=payload, **get_args) |
|
337 |
except RequestFailed, e: |
|
338 |
raise ROAException(e) |
|
339 |
else: |
|
340 |
record_exists = False |
|
341 |
resource = Resource(self.get_resource_url_list()) |
|
342 |
try: |
|
343 |
logger.debug(u"""Creating : "%s" through %s |
|
344 |
with payload "%s" and GET args "%s" """ % ( |
|
345 |
force_unicode(self), |
|
346 |
force_unicode(resource.uri), |
|
347 |
force_unicode(payload), |
|
348 |
force_unicode(get_args))) |
|
349 |
response = resource.post(payload=payload, **get_args) |
|
350 |
except RequestFailed, e: |
|
351 |
raise ROAException(e) |
|
352 |
||
353 |
for local_name, remote_name in ROA_MODEL_NAME_MAPPING: |
|
354 |
response = response.replace(remote_name, local_name) |
|
355 |
||
356 |
response = force_unicode(response).encode(settings.DEFAULT_CHARSET) |
|
357 |
deserializer = serializers.get_deserializer(ROA_FORMAT) |
|
358 |
if hasattr(deserializer, 'deserialize_object'): |
|
359 |
result = deserializer(response).deserialize_object(response) |
|
360 |
else: |
|
361 |
result = deserializer(response).next() |
|
362 |
||
363 |
self.id = int(result.object.id) |
|
364 |
self = result.object |
|
277 |
365 |
|
278 |
serializer = serializers.get_serializer(ROA_FORMAT) |
|
279 |
if hasattr(serializer, 'serialize_object'): |
|
280 |
payload = serializer().serialize_object(self) |
|
281 |
else: |
|
282 |
payload = {} |
|
283 |
for field in meta.local_fields: |
|
284 |
||
285 |
# Handle FK fields |
|
286 |
if isinstance(field, models.ForeignKey): |
|
287 |
field_attr = getattr(self, field.name) |
|
288 |
if field_attr is None: |
|
289 |
payload[field.attname] = None |
|
290 |
else: |
|
291 |
payload[field.attname] = field_attr.id |
|
292 |
||
293 |
# Handle all other fields |
|
294 |
else: |
|
295 |
payload[field.name] = field.value_to_string(self) |
|
296 |
||
297 |
# Handle M2M relations in case of update |
|
298 |
if force_update or pk_set and not self.id is None: |
|
299 |
for field in meta.many_to_many: |
|
300 |
# First try to get ids from var set in query's add/remove/clear |
|
301 |
if hasattr(self, '%s_updated_ids' % field.attname): |
|
302 |
field_ids = getattr(self, '%s_updated_ids' % field.attname) |
|
303 |
else: |
|
304 |
field_ids = [obj.id for obj in field.value_from_object(self)] |
|
305 |
payload[field.attname] = ','.join(smart_unicode(id) for id in field_ids) |
|
306 |
||
307 |
if force_update or pk_set and not self.id is None: |
|
308 |
resource = Resource(self.get_resource_url_detail()) |
|
309 |
try: |
|
310 |
logger.debug(u"""Modifying : "%s" through %s |
|
311 |
with payload "%s" and GET args "%s" """ % ( |
|
312 |
force_unicode(self), |
|
313 |
force_unicode(resource.uri), |
|
314 |
force_unicode(payload), |
|
315 |
force_unicode(get_args))) |
|
316 |
response = resource.put(payload=payload, **get_args) |
|
317 |
except RequestFailed, e: |
|
318 |
raise ROAException(e) |
|
319 |
else: |
|
320 |
resource = Resource(self.get_resource_url_list()) |
|
321 |
try: |
|
322 |
logger.debug(u"""Creating : "%s" through %s |
|
323 |
with payload "%s" and GET args "%s" """ % ( |
|
324 |
force_unicode(self), |
|
325 |
force_unicode(resource.uri), |
|
326 |
force_unicode(payload), |
|
327 |
force_unicode(get_args))) |
|
328 |
response = resource.post(payload=payload, **get_args) |
|
329 |
except RequestFailed, e: |
|
330 |
raise ROAException(e) |
|
331 |
||
332 |
for local_name, remote_name in ROA_MODEL_NAME_MAPPING: |
|
333 |
response = response.replace(remote_name, local_name) |
|
334 |
||
335 |
response = force_unicode(response).encode(settings.DEFAULT_CHARSET) |
|
336 |
deserializer = serializers.get_deserializer(ROA_FORMAT) |
|
337 |
if hasattr(deserializer, 'deserialize_object'): |
|
338 |
result = deserializer(response).deserialize_object(response) |
|
339 |
else: |
|
340 |
result = deserializer(response).next() |
|
341 |
||
342 |
self.id = int(result.object.id) |
|
343 |
self = result.object |
|
366 |
if origin: |
|
367 |
signals.post_save.send(sender=origin, instance=self, |
|
368 |
created=(not record_exists), raw=raw) |
|
344 |
369 |
|
345 |
370 |
save_base.alters_data = True |
346 |
371 |
Up to file-list examples/django_roa_client/models.py:
| … | … | @@ -116,3 +116,13 @@ class RemotePageWithNamedRelations(Model |
116 |
116 |
@staticmethod |
117 |
117 |
def get_resource_url_list(): |
118 |
118 |
return u'http://127.0.0.1:8081/django_roa_server/remotepagewithnamedrelations/' |
119 |
||
120 |
||
121 |
class RemotePageWithProxy(RemotePage): |
|
122 |
||
123 |
class Meta: |
|
124 |
proxy = True |
|
125 |
||
126 |
@staticmethod |
|
127 |
def get_resource_url_list(): |
|
128 |
return u'http://127.0.0.1:8081/django_roa_server/remotepagewithproxy/' |
Up to file-list examples/django_roa_client/tests.py:
| … | … | @@ -41,7 +41,7 @@ from django_roa.remoteauth.models import |
41 |
41 |
from django_roa_client.models import RemotePage, RemotePageWithManyFields, \ |
42 |
42 |
RemotePageWithBooleanFields, RemotePageWithRelations, \ |
43 |
43 |
RemotePageWithCustomSlug, RemotePageWithOverriddenUrls, \ |
44 |
RemotePageWithNamedRelations |
|
44 |
RemotePageWithNamedRelations, RemotePageWithProxy |
|
45 |
45 |
from django_roa_client.forms import TestForm, RemotePageForm |
46 |
46 |
|
47 |
47 |
class ROATestCase(TestCase): |
| … | … | @@ -409,6 +409,21 @@ class ROARelationsTests(ROATestCase): |
409 |
409 |
another_remote_page.delete() |
410 |
410 |
remote_page.delete() |
411 |
411 |
|
412 |
def test_proxy_relation(self): |
|
413 |
remote_page = RemotePage.objects.create(title=u'A remote page') |
|
414 |
proxy_remote_page = RemotePageWithProxy.objects.create(title=u'A proxy remote page') |
|
415 |
self.assertEqual(repr(remote_page), '<RemotePage: A remote page (1)>') |
|
416 |
self.assertEqual(repr(proxy_remote_page), '<RemotePageWithProxy: A proxy remote page (2)>') |
|
417 |
self.assertEqual(repr(RemotePage.objects.all()), '[<RemotePage: A remote page (1)>, <RemotePage: A proxy remote page (2)>]') |
|
418 |
self.assertEqual(repr(RemotePageWithProxy.objects.all()), '[<RemotePage: A remote page (1)>, <RemotePage: A proxy remote page (2)>]') |
|
419 |
proxy_remote_page.title = u'A modified proxy remote page' |
|
420 |
proxy_remote_page.save() |
|
421 |
proxy_remote_page = RemotePageWithProxy.objects.get(id=2) |
|
422 |
self.assertEqual(repr(proxy_remote_page), '<RemotePage: A modified proxy remote page (2)>') |
|
423 |
self.assertEqual(repr(RemotePage.objects.all()[1]), '<RemotePage: A modified proxy remote page (2)>') |
|
424 |
proxy_remote_page.delete() |
|
425 |
remote_page.delete() |
|
426 |
||
412 |
427 |
|
413 |
428 |
class ROAQuerysetTests(ROATestCase): |
414 |
429 |
Up to file-list examples/django_roa_server/urls.py:
| … | … | @@ -49,6 +49,7 @@ urlpatterns = patterns('', |
49 |
49 |
url(r'^django_roa_server/remotepagewithoverriddenurls/count/$', remote_pages_with_overridden_urls_count), |
50 |
50 |
url(r'^django_roa_server/remotepagewithrelations/count/$', remote_pages_with_relations_count), |
51 |
51 |
url(r'^django_roa_server/remotepagewithnamedrelations/count/$', remote_pages_with_named_relations_count), |
52 |
url(r'^django_roa_server/remotepagewithproxy/count/$', remote_pages_count), |
|
52 |
53 |
|
53 |
54 |
# Remote pages |
54 |
55 |
url(r'^django_roa_server/remotepage/?(?P<id>\d+)?/?$', remote_pages), |
| … | … | @@ -58,6 +59,7 @@ urlpatterns = patterns('', |
58 |
59 |
url(r'^django_roa_server/remotepagewithoverriddenurls/?(?P<object_slug>[-\w]+)?/?$', remote_pages_with_overridden_urls), |
59 |
60 |
url(r'^django_roa_server/remotepagewithrelations/?(?P<id>\d+)?/?$', remote_pages_with_relations), |
60 |
61 |
url(r'^django_roa_server/remotepagewithnamedrelations/?(?P<id>\d+)?/?$', remote_pages_with_named_relations), |
62 |
url(r'^django_roa_server/remotepagewithproxy/?(?P<id>\d+)?/?$', remote_pages), |
|
61 |
63 |
|
62 |
64 |
# Auth application |
63 |
65 |
url(r'^auth/user/?(?P<id>\d+)?/?$', users), |
