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.

Clone this repository (size: 553.5 KB): HTTPS / SSH
$ hg clone http://code.welldev.org/django-roa/

Changed (Δ2.6 KB):

raw changeset »

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 not cls:
254
        if cls is None:
249
255
            cls = self.__class__
250
            meta = self._meta
251
            signal = True
252
            signals.pre_save.send(sender=self.__class__, instance=self, raw=raw)
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
        if not raw:
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 getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None:
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),