Login

RESTful class dispatch

Author:
Phoenix
Posted:
June 19, 2009
Language:
Python
Version:
1.0
Score:
1 (after 1 ratings)

Yet another implementation of class based RESTful dispatch. This particular implementation features:

  • You do not have to call init from the derived classes.
  • Avoids metaclass which (in our environment) led to unexpected method override behavior.
  • Method names match the google webapp API.
  • One new instance per request to reduce errors in multi-threaded code.

Snippets of inspiration: 436 437 1071 1072 * 1226

 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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
from django.http import HttpResponse
from django.http import HttpResponseNotAllowed

METHODS = sorted(('get', 'post', 'head', 'options', 'put', 'delete'))

class ResourceView(object):
    """\
    @brief Base class which implements restful dispatch on a resource

    To use this class you derive from this class and provide an
    implementation for any of the methods in the METHODS tuple found
    at file scope in this file. 

    Define a class which extends ResourceView.

    class Region(ResourceView):
        def get(self, request):
            ...
        def post(self, request):
            ...

    And then in urls.py, dispatch to a new class instance via the call:

    urlpatterns = patterns(
        '',
        (r'^region/$', views.Region.dispatch))

    The call to dispatch call will automatically generate a private Region
    instance which will live for the lengh of the HTTP request/responce
    cycle and then discarded.

    The ResourceView class will generate a reasonable default
    implementation of head() if your class does not provide one and
    defines get().

    *NOTE: Any callable without a leading underscore is considered part of
    the public API and may be called by the dispatch. Make sure you
    prepend underscores in implementation methods which should not be
    accepting direct connections from a client.
    """
    @classmethod
    def dispatch(klass, request, *args, **kwargs):
        """\
        @brief classmethod which is the django callable to call during
        RESTful dispatch.

        @param klass The derived class we are calling.
        @param request The Django request object.
        @return Returns the response in klass in the matching
        request.method.
        """
        return klass().__dispatch(request, *args, **kwargs)

    def __not_allowed(self):
        """\
        @breif Generate the HTTP Not Allowed message for the client.
        """
        allow = []
        for method in METHODS:
            if hasattr(self, method):
                allow.append(method)
        if 'get' in allow and 'head' not in allow:
            allow.append('head')
        return HttpResponseNotAllowed(k.upper() for k in allow)        

    def __default_head(self, request, *args, **kwargs):
        """\
        @brief Simple implementation of HEAD.

        This is implemented as a private method because we cannot add
        automatic support for HEAD unless the instance supports GET
        which has to be detected at runtime in this code structure.
        """
        response = self.get(request, *args, **kwargs)
        if not isinstance(response, HttpResponse): 
            return ''
        response.content = ''
        return response

    def __no_method(self, request, *args, **kwargs):
        """\
        @brief This method is called when the derived class does not
        implement the HTTP method invoked on the object.
        """
        if request.method == 'HEAD' and hasattr(self, 'get'):
            return self.__default_head(request, *args, **kwargs)
        return self.__not_allowed()

    def __dispatch(self, request, *args, **kwargs):
        """\
        @brief Class level implementation for classmethod dispatch.
        """
        method = request.method.lower()
        if method.startswith('_'):
            # do not allow people who make up bad http methods to call
            # into the private implementation details. Phoenix 2009-05-21
            return self.__not_allowed()
        return getattr(self, method, self.__no_method)(request, *args, **kwargs)

More like this

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

Comments

Please login first before commenting.