The Resource
class is a way of writing a Django view as a class. I've done this as part of an effort to write easier-to-understand RESTful apps, as this allows the grouping of similar views as different types of operation on a resource. Essentially, the solution goes something like this:
- Views have to be callables which return HttpResponse objects.
- The problem with views as classes is that calling a view class returns an instance of the view class, not HttpResponse.
- Solution: have the VC (view class) a subclass of HttpResponse. This way, 'calling' the class will return a HttpResponse instance.
The Resource
class performs a dispatch on the request method, so that a resource can be retrieved, created/updated and deleted by writing 'get', 'put' and 'delete' methods on a subclass of Resource
.
A general Book
class shows how this might work for the case of 'books' in a system:
class Book(Resource):
def get(self, request, book_name):
book = myapp.models.Book.objects.get(name=book_name)
return render_to_response('book_template.html', {'book': book})
def put(self, request, book_name):
new_book, created = get_or_create(myapp.models.Book, name=book_name)
new_book.data = request.raw_post_data
if created:
return HttpResponse(status=201)
return HttpResponse(status=200)
def delete(self, request, book_name):
book = myapp.models.Book.objects.get(name=book_name)
book.delete()
return HttpResponse()
As you can see, classes can return responses, and these will be merged back into the returned response by the Resource._update
method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | import string
from django.http import HttpResponse, HttpResponseNotAllowed
class Resource(HttpResponse):
"""Represents a single resource within a RESTful web service.
The ``Resource`` class should be used to represent a single resource
within a RESTful web service. It exists to be subclassed for each
resource within the service, and these subclasses should define a method
for each possible HTTP request method on the so"""
def __init__(self, request, *args, **kwargs):
"""Instantiate a ``Resource`` instance.
This method overrides ``HttpResponse.__init__``, providing an
alternative way of serving views. It calls the overridden method
first, to handle the initialization, and then performs a dispatch
on the HTTP request method. The return values of these methods are
then merged back into the current ``HttpResponse`` instance.
Because this is called like a view function, this method accepts
a request object and any other positional and/or keyword arguments,
These will be passed to the methods defined on a subclass, so those
methods should support any arguments given.
If the given HTTP request method is not defined for a subclass, then
a 405 'Method Not Allowed' response is returned, along with a list
of allowed methods (obtained via introspection)."""
HttpResponse.__init__(self)
if hasattr(self, request.method.lower()):
value = getattr(self, request.method.lower())(request,
*args, **kwargs)
if isinstance(value, HttpResponse):
self._update(value)
elif hasattr(self, 'run'):
value = self.run(request, *args, **kwargs)
if isinstance(value, HttpResponse):
self._update(value)
else:
allowed_methods = []
for attr in dir(self):
if set(attr).issubset(set(string.lowercase)):
allowed_methods.append(attr.upper())
self._update(HttpResponseNotAllowed(sorted(allowed_methods)))
def _update(self, response):
"""Merge the info from another response with this instance.
This method simply copies the attributes from the given response to
this instance, with the exceptions of the ``_headers`` and ``cookies``
dictionaries, whose ``update`` methods are called. This means that any
headers or cookies which are present in this response but not the
argument are preserved."""
self._charset = response._charset
self._is_string = response._is_string
self._container = response._container
self._headers.update(response._headers)
self.cookies.update(response.cookies)
self.status_code = response.status_code
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 1 week ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Shouldn't you be checking that attr is in a list of allowed HTTP methods (GET/POST/PUT etc)? Otherwise the client-side could execute arbitrary methods of any subclass of Resource.
#
Please login first before commenting.