Login

Resource

Author:
zvoase
Posted:
September 21, 2008
Language:
Python
Version:
1.0
Score:
3 (after 3 ratings)

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

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

bryhoyt (on March 6, 2010):

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.