Statistics
| Branch: | Tag: | Revision:

root / ui / userdata / rest.py @ c72a830d

History | View | Annotate | Download (6.8 kB)

1
from django import http
2
from django.template import RequestContext, loader
3
from django.utils import simplejson as json
4
from django.core import serializers
5
from django.core.urlresolvers import reverse
6

    
7
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
8

    
9
# base view class
10
# https://github.com/bfirsh/django-class-based-views/blob/master/class_based_views/base.py
11
class View(object):
12
    """
13
    Intentionally simple parent class for all views. Only implements
14
    dispatch-by-method and simple sanity checking.
15
    """
16

    
17
    method_names = ['GET', 'POST', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE']
18

    
19
    def __init__(self, *args, **kwargs):
20
        """
21
        Constructor. Called in the URLconf; can contain helpful extra
22
        keyword arguments, and other things.
23
        """
24
        # Go through keyword arguments, and either save their values to our
25
        # instance, or raise an error.
26
        for key, value in kwargs.items():
27
            if key in self.method_names:
28
                raise TypeError(u"You tried to pass in the %s method name as a "
29
                                u"keyword argument to %s(). Don't do that."
30
                                % (key, self.__class__.__name__))
31
            if hasattr(self, key):
32
                setattr(self, key, value)
33
            else:
34
                raise TypeError(u"%s() received an invalid keyword %r" % (
35
                    self.__class__.__name__,
36
                    key,
37
                ))
38

    
39
    @classmethod
40
    def as_view(cls, *initargs, **initkwargs):
41
        """
42
        Main entry point for a request-response process.
43
        """
44
        def view(request, *args, **kwargs):
45
            self = cls(*initargs, **initkwargs)
46
            return self.dispatch(request, *args, **kwargs)
47
        return view
48

    
49
    def dispatch(self, request, *args, **kwargs):
50
        # Try to dispatch to the right method for that; if it doesn't exist,
51
        # raise a big error.
52
        if hasattr(self, request.method.upper()):
53
            self.request = request
54
            self.args = args
55
            self.kwargs = kwargs
56
            data = request.raw_post_data
57

    
58
            if request.method.upper() in ['POST', 'PUT']:
59
                # Expect json data
60
                if request.META.get('CONTENT_TYPE').startswith('application/json'):
61
                    try:
62
                        data = json.loads(data)
63
                    except ValueError:
64
                        raise http.HttpResponseServerError('Invalid JSON data.')
65
                else:
66
                    raise http.HttpResponseServerError('Unsupported Content-Type.')
67
            try:
68
                return getattr(self, request.method.upper())(request, data, *args, **kwargs)
69
            except ValidationError, e:
70
                # specific response for validation errors
71
                return http.HttpResponseServerError(json.dumps({'errors':
72
                    e.message_dict, 'non_field_key':
73
                    NON_FIELD_ERRORS }))
74

    
75
        else:
76
            allowed_methods = [m for m in self.method_names if hasattr(self, m)]
77
            return http.HttpResponseNotAllowed(allowed_methods)
78

    
79
class JSONRestView(View):
80
    """
81
    Class that provides helpers to produce a json response
82
    """
83

    
84
    url_name = None
85
    def __init__(self, url_name, *args, **kwargs):
86
        self.url_name = url_name
87
        return super(JSONRestView, self).__init__(*args, **kwargs)
88

    
89
    def update_instance(self, i, data, exclude_fields=[]):
90
        update_keys = data.keys()
91
        for field in i._meta.get_all_field_names():
92
            if field in update_keys and (field not in exclude_fields):
93
                i.__setattr__(field, data[field])
94

    
95
        return i
96

    
97
    def instance_to_dict(self, i, exclude_fields=[]):
98
        """
99
        Convert model instance to python dict
100
        """
101
        d = {}
102
        d['uri'] = reverse(self.url_name, kwargs={'id': i.pk})
103

    
104
        for field in i._meta.get_all_field_names():
105
            if field in exclude_fields:
106
                continue
107

    
108
            d[field] = i.__getattribute__(field)
109
        return d
110

    
111
    def qs_to_dict_iter(self, qs, exclude_fields=[]):
112
        """
113
        Convert queryset to an iterator of model instances dicts
114
        """
115
        for i in qs:
116
            yield self.instance_to_dict(i, exclude_fields)
117

    
118
    def json_response(self, data):
119
        return http.HttpResponse(json.dumps(data), mimetype="application/json")
120

    
121
class ResourceView(JSONRestView):
122
    method_names = ['GET', 'POST', 'PUT', 'DELETE']
123

    
124
    model = None
125
    exclude_fields = []
126

    
127
    def queryset(self):
128
        return self.model.objects.all()
129

    
130
    def instance(self):
131
        """
132
        Retrieve selected instance based on url parameter
133

134
        id parameter should be set in urlpatterns expression
135
        """
136
        try:
137
            return self.queryset().get(pk=self.kwargs.get("id"))
138
        except self.model.DoesNotExist:
139
            raise http.Http404
140

    
141
    def GET(self, request, data, *args, **kwargs):
142
        return self.json_response(self.instance_to_dict(self.instance(),
143
            self.exclude_fields))
144

    
145
    def PUT(self, request, data, *args, **kwargs):
146
        instance = self.instance()
147
        self.update_instance(instance, data, self.exclude_fields)
148
        instance.full_clean()
149
        instance.save()
150
        return self.GET(request, data, *args, **kwargs)
151

    
152
    def DELETE(self, request, data, *args, **kwargs):
153
        self.instance().delete()
154
        return self.json_response("")
155

    
156

    
157
class CollectionView(JSONRestView):
158
    method_names = ['GET', 'POST']
159

    
160
    model = None
161
    exclude_fields = []
162

    
163
    def queryset(self):
164
        return self.model.objects.all()
165

    
166
    def GET(self, request, data, *args, **kwargs):
167
        return self.json_response(list(self.qs_to_dict_iter(self.queryset(),
168
            self.exclude_fields)))
169

    
170
    def POST(self, request, data, *args, **kwargs):
171
        instance = self.model()
172
        self.update_instance(instance, data, self.exclude_fields)
173
        instance.full_clean()
174
        instance.save()
175
        return self.json_response(self.instance_to_dict(instance,
176
            self.exclude_fields))
177

    
178
class UserResourceView(ResourceView):
179
    """
180
    Filter resource queryset for request user entries
181
    """
182
    def queryset(self):
183
        return super(UserResourceView,
184
                self).queryset().filter(user=self.request.user)
185

    
186
class UserCollectionView(CollectionView):
187
    """
188
    Filter collection queryset for request user entries
189
    """
190
    def queryset(self):
191
        return super(UserCollectionView, self).queryset().filter(user=self.request.user)
192

    
193
    def POST(self, request, data, *args, **kwargs):
194
        instance = self.model()
195
        self.update_instance(instance, data, self.exclude_fields)
196
        instance.user = request.user
197
        instance.full_clean()
198
        instance.save()
199
        return self.json_response(self.instance_to_dict(instance,
200
            self.exclude_fields))
201