Permalink
Browse files

Merge branch 'release/0.4'

  • Loading branch information...
2 parents aa4da45 + e52aa99 commit f199c598f506cb386e7cdbdd46d0941e3d564526 @KayEss committed Aug 10, 2011
View
@@ -1,3 +1,11 @@
+2011-08-058 Kirit Saelensminde <kirit@felspar.com>
+ Added a server side implementation for updating instances.
+ The model `get` method can now be passed any unique combination of attributes and the right instance will be returned.
+
+2011-08-05 Kirit Saelensminde <kirit@felspar.com>
+ The server operations can now implement either `operation` or the HTTP method that they need to execute with. Unsupported methods will return a 403.
+ Refactored the layout of the operations.
+
2011-07-28 Kirit Saelensminde <kirit@felspar.com>
Added some simple mocking capability so that client code that uses Slumber can be tested.
View
@@ -7,7 +7,7 @@ def read(fname):
setup(
name = "django_slumber",
- version = "0.3.2",
+ version = "0.4",
author = "Kirit Saelensminde",
author_email = "kirit@felspar.com",
description = ("RESTful data connector for Django"),
@@ -3,6 +3,10 @@
"""
from django.core.urlresolvers import reverse
+def _forbidden(_request, response, *_):
+ """Return an error to say that the method type is not allowed.
+ """
+ response['_meta']['status'] = 403
class ModelOperation(object):
"""Base class for model operations.
@@ -14,6 +18,15 @@ def __init__(self, model, name):
self.regex = ''
self.path = model.path + name + '/'
+ def operation(self, request, response, *args):
+ """Perform the requested operation in the server.
+ """
+ if request.method in ['GET', 'POST', 'PUT', 'DELETE']:
+ return getattr(self, request.method.lower(), _forbidden)(
+ request, response, *args)
+ else:
+ _forbidden(request, response)
+
class InstanceOperation(ModelOperation):
"""Base class for operations on instances.
@@ -22,41 +35,3 @@ class InstanceOperation(ModelOperation):
def __init__(self, model, name):
super(InstanceOperation, self).__init__(model, name)
self.regex = '([^/]+)/'
-
-
-class InstanceList(ModelOperation):
- """Allows access to the instances.
- """
- def operation(self, request, response, _appname, _modelname):
- """Return a paged set of instances for this model.
- """
- root = reverse('slumber.server.views.get_applications')
- response['model'] = root + self.model.path
-
- query = self.model.model.objects.order_by('-pk')
- if request.GET.has_key('start_after'):
- query = query.filter(pk__lt=request.GET['start_after'])
-
- response['page'] = [
- dict(pk=o.pk, display=unicode(o),
- data=root + self.model.path + 'data/%s/' % o.pk)
- for o in query[:10]]
- if len(response['page']) > 0:
- response['next_page'] = root +self.model.path + \
- 'instances/?start_after=%s' % response['page'][-1]['pk']
-
-
-class CreateInstance(ModelOperation):
- """Allows for the creation of new instances.
- """
- def operation(self, request, response, _appname, _modelname):
- """Perform the object creation.
- """
- if request.method == 'POST':
- response['created'] = True
- instance = self.model.model(**dict([(k, str(v))
- for k, v in request.POST.items()]))
- instance.save()
- response['pk'] = instance.pk
- else:
- response['created'] = False
@@ -0,0 +1,17 @@
+"""
+ Implements creation of an object.
+"""
+from slumber.operations import ModelOperation
+
+
+class CreateInstance(ModelOperation):
+ """Allows for the creation of new instances.
+ """
+ def post(self, request, response, _appname, _modelname):
+ """Perform the object creation.
+ """
+ instance = self.model.model(**dict([(k, str(v))
+ for k, v in request.POST.items()]))
+ instance.save()
+ response['created'] = True
+ response['pk'] = instance.pk
@@ -2,30 +2,16 @@
Implements the server side for the instance operators.
"""
from django.core.urlresolvers import reverse
-from django.http import HttpResponseRedirect
from slumber._caches import MODEL_CACHE
from slumber.json import to_json_data
-from slumber.operations import InstanceOperation, ModelOperation
-
-
-class DereferenceInstance(ModelOperation):
- """Given a primary key (or other unique set of attributes) redirects
- to the instance item."""
- def operation(self, request, _response, _appname, _modelname):
- """Work out the correct data URL for an instance we're going to
- search for.
- """
- root = reverse('slumber.server.views.get_applications')
- instance = self.model.model.objects.get(pk=request.GET['pk'])
- return HttpResponseRedirect(
- root + self.model.path + 'data/%s/' % instance.pk)
+from slumber.operations import InstanceOperation
class InstanceData(InstanceOperation):
"""Return the instance data.
"""
- def operation(self, _request, response, _appname, _modelname, pk):
+ def get(self, _request, response, _appname, _modelname, pk):
"""Implement the fetching of attribute data for an instance.
"""
root = reverse('slumber.server.views.get_applications')
@@ -51,7 +37,7 @@ def __init__(self, model, name, field):
self.field = field
self.regex = '([^/]+)/(%s)/' % field
- def operation(self, request, response, _appname, _modelname, pk, _dataset):
+ def get(self, request, response, _appname, _modelname, pk, _dataset):
"""Return one page of the array data.
"""
root = reverse('slumber.server.views.get_applications')
@@ -0,0 +1,27 @@
+"""
+ Implements a listing of all instances for a given model.
+"""
+from slumber.operations import ModelOperation
+from slumber.server import get_slumber_root
+
+
+class InstanceList(ModelOperation):
+ """Allows access to the instances.
+ """
+ def get(self, request, response, _appname, _modelname):
+ """Return a paged set of instances for this model.
+ """
+ root = get_slumber_root()
+ response['model'] = root + self.model.path
+
+ query = self.model.model.objects.order_by('-pk')
+ if request.GET.has_key('start_after'):
+ query = query.filter(pk__lt=request.GET['start_after'])
+
+ response['page'] = [
+ dict(pk=o.pk, display=unicode(o),
+ data=root + self.model.path + 'data/%s/' % o.pk)
+ for o in query[:10]]
+ if len(response['page']) > 0:
+ response['next_page'] = root +self.model.path + \
+ 'instances/?start_after=%s' % response['page'][-1]['pk']
@@ -0,0 +1,23 @@
+"""
+ Allows the data URL to be found for a given object.
+"""
+from django.http import HttpResponseRedirect
+
+from slumber.operations import ModelOperation
+from slumber.server import get_slumber_root
+
+
+class DereferenceInstance(ModelOperation):
+ """Given a primary key (or other unique set of attributes) redirects
+ to the instance item.
+ """
+ def get(self, request, _response, _appname, _modelname):
+ """Work out the correct data URL for an instance we're going to
+ search for.
+ """
+ root = get_slumber_root()
+ instance = self.model.model.objects.get(
+ **dict([(k, request.GET[k])
+ for k in request.GET.keys()]))
+ return HttpResponseRedirect(
+ root + self.model.path + 'data/%s/' % instance.pk)
@@ -0,0 +1,21 @@
+"""
+ Implements updating of instances.
+"""
+from django.http import HttpResponseRedirect
+
+from slumber.server import get_slumber_root
+from slumber.operations import InstanceOperation
+
+
+class UpdateInstance(InstanceOperation):
+ """Update the attributes of a given instance.
+ """
+ def post(self, request, _response, _appname, _modelname, pk):
+ """Perform the update.
+ """
+ instance = self.model.model.objects.get(pk=pk)
+ for k, v in request.POST.items():
+ setattr(instance, k, v)
+ instance.save()
+ return HttpResponseRedirect(
+ get_slumber_root() + self.model.path + 'data/%s/' % instance.pk)
@@ -1,3 +1,10 @@
"""
The standard Slumber server implementation.
"""
+from django.core.urlresolvers import reverse
+
+
+def get_slumber_root():
+ """Returns the location of the Slumber on this server.
+ """
+ return reverse('slumber.server.views.get_applications')
@@ -1,4 +0,0 @@
-from django.core.urlresolvers import reverse
-
-def get_slumber_root():
- return reverse('slumber.server.views.get_applications')#settings.SLUMBER_ROOT
View
@@ -14,10 +14,10 @@ def view_handler(view):
def wrapper(request, *args, **kwargs):
"""The decorated implementation.
"""
- response = {}
+ response = {'_meta': dict(status=200, message='OK')}
http_response = view(request, response, *args, **kwargs)
if http_response:
return http_response
- else:
- return HttpResponse(dumps(response, indent=4), 'text/plain')
+ return HttpResponse(dumps(response, indent=4), 'text/plain',
+ status=response['_meta']['status'])
return wrapper
View
@@ -1,29 +1,30 @@
-"""
- Implements the server side wrapper for a Django model.
-"""
-from django.db.models import ForeignKey
-from django.db.models.fields import FieldDoesNotExist
-
-from slumber._caches import MODEL_CACHE
-from slumber.operations import InstanceList, CreateInstance
-from slumber.operations.instancedata import DereferenceInstance, \
- InstanceData, InstanceDataArray
-
-from slumber.server.configuration import get_slumber_root
-
-
-class DjangoModel(object):
- """Describes a Django model.
- """
- def __init__(self, app, model_instance):
- MODEL_CACHE[model_instance] = self
- self.app = app
- self.model = model_instance
- self.name = model_instance.__name__
- self.path = app.path + '/' + self.name + '/'
-
- self._fields, self._data_arrays = {}, []
-
+"""
+ Implements the server side wrapper for a Django model.
+"""
+from django.db.models import ForeignKey
+from django.db.models.fields import FieldDoesNotExist
+
+from slumber._caches import MODEL_CACHE
+from slumber.operations.create import CreateInstance
+from slumber.operations.instancedata import InstanceData, InstanceDataArray
+from slumber.operations.instancelist import InstanceList
+from slumber.operations.search import DereferenceInstance
+from slumber.operations.update import UpdateInstance
+from slumber.server import get_slumber_root
+
+
+class DjangoModel(object):
+ """Describes a Django model.
+ """
+ def __init__(self, app, model_instance):
+ MODEL_CACHE[model_instance] = self
+ self.app = app
+ self.model = model_instance
+ self.name = model_instance.__name__
+ self.path = app.path + '/' + self.name + '/'
+
+ self._fields, self._data_arrays = {}, []
+
def _get_fields_and_data_arrays(self):
"""Work out what the fields we have are.
"""
@@ -50,7 +51,8 @@ def fields(self):
fields[field] = dict(
name=field,
kind='object',
- type= get_slumber_root() + MODEL_CACHE[definition.rel.to].path,
+ type= get_slumber_root() +
+ MODEL_CACHE[definition.rel.to].path,
verbose_name=definition.verbose_name)
else:
type_name = field_type.__module__ + '.' + \
@@ -59,18 +61,20 @@ def fields(self):
kind='value', type=type_name,
verbose_name=definition.verbose_name)
return fields
-
- @property
- def data_arrays(self):
- """Return the data array fields.
- """
- self._get_fields_and_data_arrays()
- return self._data_arrays
-
- def operations(self):
- """Return all of the operations available for this model.
- """
- return [InstanceList(self, 'instances'),
- CreateInstance(self, 'create'), InstanceData(self, 'data'),
- DereferenceInstance(self, 'get')] + \
- [InstanceDataArray(self, 'data', f) for f in self.data_arrays]
+
+ @property
+ def data_arrays(self):
+ """Return the data array fields.
+ """
+ self._get_fields_and_data_arrays()
+ return self._data_arrays
+
+ def operations(self):
+ """Return all of the operations available for this model.
+ """
+ return [InstanceList(self, 'instances'),
+ CreateInstance(self, 'create'),
+ InstanceData(self, 'data'),
+ DereferenceInstance(self, 'get'),
+ UpdateInstance(self, 'update')] + \
+ [InstanceDataArray(self, 'data', f) for f in self.data_arrays]
View
@@ -3,9 +3,10 @@
"""
from django.http import HttpResponseRedirect, HttpResponseNotFound
+from slumber.server import get_slumber_root
from slumber.server.http import view_handler
from slumber.server.meta import applications, get_application
-from slumber.server.configuration import get_slumber_root
+
@view_handler
def get_applications(request, response):
@@ -21,7 +22,6 @@ def get_applications(request, response):
response['apps'] = dict([(app.name, root + app.path + '/')
for app in applications()])
-
@view_handler
def get_models(_, response, appname):
"""Return the models that comprise an application.
@@ -97,7 +97,7 @@ def test_instance_data_with_nested_data_array(self):
self.assertEqual(len(pizza.prices), 1)
self.assertEqual(len(pizza.prices[0].amounts), 3)
for a in pizza.prices[0].amounts:
- self.assertIn(a.size, ['s', 'm', 'l'])
+ self.assertTrue(a.size in ['s', 'm', 'l'], a.size)
def test_instance_no_pk(self):
pizza = client.slumber_test.Pizza.get(pk=None)
Oops, something went wrong.

0 comments on commit f199c59

Please sign in to comment.