root / snf-cyclades-app / synnefo / userdata / rest.py @ a8333def
History | View | Annotate | Download (8.5 kB)
1 | 49f50673 | Vangelis Koukis | #
|
---|---|---|---|
2 | 49f50673 | Vangelis Koukis | # Copyright 2011 GRNET S.A. All rights reserved.
|
3 | 49f50673 | Vangelis Koukis | #
|
4 | 49f50673 | Vangelis Koukis | # Redistribution and use in source and binary forms, with or
|
5 | 49f50673 | Vangelis Koukis | # without modification, are permitted provided that the following
|
6 | 49f50673 | Vangelis Koukis | # conditions are met:
|
7 | 49f50673 | Vangelis Koukis | #
|
8 | 49f50673 | Vangelis Koukis | # 1. Redistributions of source code must retain the above
|
9 | 49f50673 | Vangelis Koukis | # copyright notice, this list of conditions and the following
|
10 | 49f50673 | Vangelis Koukis | # disclaimer.
|
11 | 49f50673 | Vangelis Koukis | #
|
12 | 49f50673 | Vangelis Koukis | # 2. Redistributions in binary form must reproduce the above
|
13 | 49f50673 | Vangelis Koukis | # copyright notice, this list of conditions and the following
|
14 | 49f50673 | Vangelis Koukis | # disclaimer in the documentation and/or other materials
|
15 | 49f50673 | Vangelis Koukis | # provided with the distribution.
|
16 | 49f50673 | Vangelis Koukis | #
|
17 | 49f50673 | Vangelis Koukis | # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
18 | 49f50673 | Vangelis Koukis | # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19 | 49f50673 | Vangelis Koukis | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
20 | 49f50673 | Vangelis Koukis | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
21 | 49f50673 | Vangelis Koukis | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
22 | 49f50673 | Vangelis Koukis | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
23 | 49f50673 | Vangelis Koukis | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
24 | 49f50673 | Vangelis Koukis | # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
25 | 49f50673 | Vangelis Koukis | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
26 | 49f50673 | Vangelis Koukis | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
27 | 49f50673 | Vangelis Koukis | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28 | 49f50673 | Vangelis Koukis | # POSSIBILITY OF SUCH DAMAGE.
|
29 | 49f50673 | Vangelis Koukis | #
|
30 | 49f50673 | Vangelis Koukis | # The views and conclusions contained in the software and
|
31 | 49f50673 | Vangelis Koukis | # documentation are those of the authors and should not be
|
32 | 49f50673 | Vangelis Koukis | # interpreted as representing official policies, either expressed
|
33 | 49f50673 | Vangelis Koukis | # or implied, of GRNET S.A.
|
34 | 49f50673 | Vangelis Koukis | |
35 | eee0487e | Kostas Papadimitriou | from django import http |
36 | eee0487e | Kostas Papadimitriou | from django.utils import simplejson as json |
37 | b896d198 | Kostas Papadimitriou | from django.core.urlresolvers import reverse |
38 | 3ec71573 | Kostas Papadimitriou | from django.http import HttpResponse |
39 | eee0487e | Kostas Papadimitriou | |
40 | c72a830d | Kostas Papadimitriou | from django.core.exceptions import ValidationError, NON_FIELD_ERRORS |
41 | c72a830d | Kostas Papadimitriou | |
42 | 04a1b675 | Christos Stavrakakis | from snf_django.lib.astakos import get_user |
43 | 5facd191 | Kostas Papadimitriou | from django.conf import settings |
44 | 5facd191 | Kostas Papadimitriou | |
45 | 49f50673 | Vangelis Koukis | |
46 | eee0487e | Kostas Papadimitriou | class View(object): |
47 | eee0487e | Kostas Papadimitriou | """
|
48 | eee0487e | Kostas Papadimitriou | Intentionally simple parent class for all views. Only implements
|
49 | eee0487e | Kostas Papadimitriou | dispatch-by-method and simple sanity checking.
|
50 | eee0487e | Kostas Papadimitriou | """
|
51 | eee0487e | Kostas Papadimitriou | |
52 | eee0487e | Kostas Papadimitriou | method_names = ['GET', 'POST', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE'] |
53 | eee0487e | Kostas Papadimitriou | |
54 | eee0487e | Kostas Papadimitriou | def __init__(self, *args, **kwargs): |
55 | eee0487e | Kostas Papadimitriou | """
|
56 | eee0487e | Kostas Papadimitriou | Constructor. Called in the URLconf; can contain helpful extra
|
57 | eee0487e | Kostas Papadimitriou | keyword arguments, and other things.
|
58 | eee0487e | Kostas Papadimitriou | """
|
59 | eee0487e | Kostas Papadimitriou | # Go through keyword arguments, and either save their values to our
|
60 | eee0487e | Kostas Papadimitriou | # instance, or raise an error.
|
61 | eee0487e | Kostas Papadimitriou | for key, value in kwargs.items(): |
62 | eee0487e | Kostas Papadimitriou | if key in self.method_names: |
63 | 49f50673 | Vangelis Koukis | raise TypeError(u"You tried to pass in the %s method name as a" |
64 | 49f50673 | Vangelis Koukis | u" keyword argument to %s(). Don't do that."
|
65 | eee0487e | Kostas Papadimitriou | % (key, self.__class__.__name__))
|
66 | eee0487e | Kostas Papadimitriou | if hasattr(self, key): |
67 | eee0487e | Kostas Papadimitriou | setattr(self, key, value) |
68 | eee0487e | Kostas Papadimitriou | else:
|
69 | eee0487e | Kostas Papadimitriou | raise TypeError(u"%s() received an invalid keyword %r" % ( |
70 | eee0487e | Kostas Papadimitriou | self.__class__.__name__,
|
71 | eee0487e | Kostas Papadimitriou | key, |
72 | eee0487e | Kostas Papadimitriou | )) |
73 | eee0487e | Kostas Papadimitriou | |
74 | eee0487e | Kostas Papadimitriou | @classmethod
|
75 | eee0487e | Kostas Papadimitriou | def as_view(cls, *initargs, **initkwargs): |
76 | eee0487e | Kostas Papadimitriou | """
|
77 | eee0487e | Kostas Papadimitriou | Main entry point for a request-response process.
|
78 | eee0487e | Kostas Papadimitriou | """
|
79 | eee0487e | Kostas Papadimitriou | def view(request, *args, **kwargs): |
80 | e3ff6830 | Georgios D. Tsoukalas | get_user(request, settings.ASTAKOS_BASE_URL) |
81 | 3ec71573 | Kostas Papadimitriou | if not request.user_uniq: |
82 | 3ec71573 | Kostas Papadimitriou | return HttpResponse(status=401) |
83 | eee0487e | Kostas Papadimitriou | self = cls(*initargs, **initkwargs)
|
84 | eee0487e | Kostas Papadimitriou | return self.dispatch(request, *args, **kwargs) |
85 | eee0487e | Kostas Papadimitriou | return view
|
86 | eee0487e | Kostas Papadimitriou | |
87 | eee0487e | Kostas Papadimitriou | def dispatch(self, request, *args, **kwargs): |
88 | eee0487e | Kostas Papadimitriou | # Try to dispatch to the right method for that; if it doesn't exist,
|
89 | eee0487e | Kostas Papadimitriou | # raise a big error.
|
90 | eee0487e | Kostas Papadimitriou | if hasattr(self, request.method.upper()): |
91 | eee0487e | Kostas Papadimitriou | self.request = request
|
92 | eee0487e | Kostas Papadimitriou | self.args = args
|
93 | eee0487e | Kostas Papadimitriou | self.kwargs = kwargs
|
94 | eee0487e | Kostas Papadimitriou | data = request.raw_post_data |
95 | eee0487e | Kostas Papadimitriou | |
96 | eee0487e | Kostas Papadimitriou | if request.method.upper() in ['POST', 'PUT']: |
97 | eee0487e | Kostas Papadimitriou | # Expect json data
|
98 | 479c3051 | Ilias Tsitsimpis | if request.META.get('CONTENT_TYPE').startswith( |
99 | 479c3051 | Ilias Tsitsimpis | 'application/json'):
|
100 | eee0487e | Kostas Papadimitriou | try:
|
101 | eee0487e | Kostas Papadimitriou | data = json.loads(data) |
102 | eee0487e | Kostas Papadimitriou | except ValueError: |
103 | 479c3051 | Ilias Tsitsimpis | return \
|
104 | 479c3051 | Ilias Tsitsimpis | http.HttpResponseServerError('Invalid JSON data.')
|
105 | eee0487e | Kostas Papadimitriou | else:
|
106 | 479c3051 | Ilias Tsitsimpis | return http.HttpResponseServerError(
|
107 | 479c3051 | Ilias Tsitsimpis | 'Unsupported Content-Type.')
|
108 | c72a830d | Kostas Papadimitriou | try:
|
109 | 479c3051 | Ilias Tsitsimpis | return getattr(self, request.method.upper())( |
110 | 479c3051 | Ilias Tsitsimpis | request, data, *args, **kwargs) |
111 | c72a830d | Kostas Papadimitriou | except ValidationError, e:
|
112 | c72a830d | Kostas Papadimitriou | # specific response for validation errors
|
113 | 479c3051 | Ilias Tsitsimpis | return http.HttpResponseServerError(
|
114 | 479c3051 | Ilias Tsitsimpis | json.dumps({'errors': e.message_dict,
|
115 | 479c3051 | Ilias Tsitsimpis | 'non_field_key': NON_FIELD_ERRORS}))
|
116 | eee0487e | Kostas Papadimitriou | |
117 | eee0487e | Kostas Papadimitriou | else:
|
118 | eee0487e | Kostas Papadimitriou | allowed_methods = [m for m in self.method_names if hasattr(self, m)] |
119 | eee0487e | Kostas Papadimitriou | return http.HttpResponseNotAllowed(allowed_methods)
|
120 | eee0487e | Kostas Papadimitriou | |
121 | 49f50673 | Vangelis Koukis | |
122 | b896d198 | Kostas Papadimitriou | class JSONRestView(View): |
123 | b896d198 | Kostas Papadimitriou | """
|
124 | b896d198 | Kostas Papadimitriou | Class that provides helpers to produce a json response
|
125 | b896d198 | Kostas Papadimitriou | """
|
126 | eee0487e | Kostas Papadimitriou | |
127 | b896d198 | Kostas Papadimitriou | url_name = None
|
128 | 479c3051 | Ilias Tsitsimpis | |
129 | b896d198 | Kostas Papadimitriou | def __init__(self, url_name, *args, **kwargs): |
130 | b896d198 | Kostas Papadimitriou | self.url_name = url_name
|
131 | b896d198 | Kostas Papadimitriou | return super(JSONRestView, self).__init__(*args, **kwargs) |
132 | b896d198 | Kostas Papadimitriou | |
133 | 69836bca | Kostas Papadimitriou | def update_instance(self, i, data, exclude_fields=[]): |
134 | b896d198 | Kostas Papadimitriou | update_keys = data.keys() |
135 | b896d198 | Kostas Papadimitriou | for field in i._meta.get_all_field_names(): |
136 | 69836bca | Kostas Papadimitriou | if field in update_keys and (field not in exclude_fields): |
137 | b896d198 | Kostas Papadimitriou | i.__setattr__(field, data[field]) |
138 | b896d198 | Kostas Papadimitriou | |
139 | b896d198 | Kostas Papadimitriou | return i
|
140 | b896d198 | Kostas Papadimitriou | |
141 | b896d198 | Kostas Papadimitriou | def instance_to_dict(self, i, exclude_fields=[]): |
142 | b896d198 | Kostas Papadimitriou | """
|
143 | b896d198 | Kostas Papadimitriou | Convert model instance to python dict
|
144 | b896d198 | Kostas Papadimitriou | """
|
145 | b896d198 | Kostas Papadimitriou | d = {} |
146 | b896d198 | Kostas Papadimitriou | d['uri'] = reverse(self.url_name, kwargs={'id': i.pk}) |
147 | b896d198 | Kostas Papadimitriou | |
148 | b896d198 | Kostas Papadimitriou | for field in i._meta.get_all_field_names(): |
149 | b896d198 | Kostas Papadimitriou | if field in exclude_fields: |
150 | b896d198 | Kostas Papadimitriou | continue
|
151 | b896d198 | Kostas Papadimitriou | |
152 | b896d198 | Kostas Papadimitriou | d[field] = i.__getattribute__(field) |
153 | b896d198 | Kostas Papadimitriou | return d
|
154 | b896d198 | Kostas Papadimitriou | |
155 | b896d198 | Kostas Papadimitriou | def qs_to_dict_iter(self, qs, exclude_fields=[]): |
156 | b896d198 | Kostas Papadimitriou | """
|
157 | b896d198 | Kostas Papadimitriou | Convert queryset to an iterator of model instances dicts
|
158 | b896d198 | Kostas Papadimitriou | """
|
159 | b896d198 | Kostas Papadimitriou | for i in qs: |
160 | b896d198 | Kostas Papadimitriou | yield self.instance_to_dict(i, exclude_fields) |
161 | b896d198 | Kostas Papadimitriou | |
162 | b896d198 | Kostas Papadimitriou | def json_response(self, data): |
163 | b896d198 | Kostas Papadimitriou | return http.HttpResponse(json.dumps(data), mimetype="application/json") |
164 | b896d198 | Kostas Papadimitriou | |
165 | 49f50673 | Vangelis Koukis | |
166 | b896d198 | Kostas Papadimitriou | class ResourceView(JSONRestView): |
167 | eee0487e | Kostas Papadimitriou | method_names = ['GET', 'POST', 'PUT', 'DELETE'] |
168 | eee0487e | Kostas Papadimitriou | |
169 | eee0487e | Kostas Papadimitriou | model = None
|
170 | eee0487e | Kostas Papadimitriou | exclude_fields = [] |
171 | eee0487e | Kostas Papadimitriou | |
172 | eee0487e | Kostas Papadimitriou | def queryset(self): |
173 | eee0487e | Kostas Papadimitriou | return self.model.objects.all() |
174 | eee0487e | Kostas Papadimitriou | |
175 | eee0487e | Kostas Papadimitriou | def instance(self): |
176 | eee0487e | Kostas Papadimitriou | """
|
177 | eee0487e | Kostas Papadimitriou | Retrieve selected instance based on url parameter
|
178 | eee0487e | Kostas Papadimitriou |
|
179 | eee0487e | Kostas Papadimitriou | id parameter should be set in urlpatterns expression
|
180 | eee0487e | Kostas Papadimitriou | """
|
181 | eee0487e | Kostas Papadimitriou | try:
|
182 | eee0487e | Kostas Papadimitriou | return self.queryset().get(pk=self.kwargs.get("id")) |
183 | eee0487e | Kostas Papadimitriou | except self.model.DoesNotExist: |
184 | eee0487e | Kostas Papadimitriou | raise http.Http404
|
185 | eee0487e | Kostas Papadimitriou | |
186 | eee0487e | Kostas Papadimitriou | def GET(self, request, data, *args, **kwargs): |
187 | 479c3051 | Ilias Tsitsimpis | return self.json_response( |
188 | 479c3051 | Ilias Tsitsimpis | self.instance_to_dict(self.instance(), self.exclude_fields)) |
189 | eee0487e | Kostas Papadimitriou | |
190 | 69836bca | Kostas Papadimitriou | def PUT(self, request, data, *args, **kwargs): |
191 | b896d198 | Kostas Papadimitriou | instance = self.instance()
|
192 | 69836bca | Kostas Papadimitriou | self.update_instance(instance, data, self.exclude_fields) |
193 | c72a830d | Kostas Papadimitriou | instance.full_clean() |
194 | b896d198 | Kostas Papadimitriou | instance.save() |
195 | b896d198 | Kostas Papadimitriou | return self.GET(request, data, *args, **kwargs) |
196 | eee0487e | Kostas Papadimitriou | |
197 | eee0487e | Kostas Papadimitriou | def DELETE(self, request, data, *args, **kwargs): |
198 | eee0487e | Kostas Papadimitriou | self.instance().delete()
|
199 | b896d198 | Kostas Papadimitriou | return self.json_response("") |
200 | eee0487e | Kostas Papadimitriou | |
201 | eee0487e | Kostas Papadimitriou | |
202 | b896d198 | Kostas Papadimitriou | class CollectionView(JSONRestView): |
203 | b896d198 | Kostas Papadimitriou | method_names = ['GET', 'POST'] |
204 | eee0487e | Kostas Papadimitriou | |
205 | eee0487e | Kostas Papadimitriou | model = None
|
206 | eee0487e | Kostas Papadimitriou | exclude_fields = [] |
207 | eee0487e | Kostas Papadimitriou | |
208 | eee0487e | Kostas Papadimitriou | def queryset(self): |
209 | eee0487e | Kostas Papadimitriou | return self.model.objects.all() |
210 | eee0487e | Kostas Papadimitriou | |
211 | eee0487e | Kostas Papadimitriou | def GET(self, request, data, *args, **kwargs): |
212 | 479c3051 | Ilias Tsitsimpis | return self.json_response( |
213 | 479c3051 | Ilias Tsitsimpis | list(self.qs_to_dict_iter(self.queryset(), self.exclude_fields))) |
214 | eee0487e | Kostas Papadimitriou | |
215 | b896d198 | Kostas Papadimitriou | def POST(self, request, data, *args, **kwargs): |
216 | 69836bca | Kostas Papadimitriou | instance = self.model()
|
217 | 69836bca | Kostas Papadimitriou | self.update_instance(instance, data, self.exclude_fields) |
218 | c72a830d | Kostas Papadimitriou | instance.full_clean() |
219 | 69836bca | Kostas Papadimitriou | instance.save() |
220 | 479c3051 | Ilias Tsitsimpis | return self.json_response( |
221 | 479c3051 | Ilias Tsitsimpis | self.instance_to_dict(instance, self.exclude_fields)) |
222 | eee0487e | Kostas Papadimitriou | |
223 | 49f50673 | Vangelis Koukis | |
224 | eee0487e | Kostas Papadimitriou | class UserResourceView(ResourceView): |
225 | eee0487e | Kostas Papadimitriou | """
|
226 | eee0487e | Kostas Papadimitriou | Filter resource queryset for request user entries
|
227 | eee0487e | Kostas Papadimitriou | """
|
228 | eee0487e | Kostas Papadimitriou | def queryset(self): |
229 | eee0487e | Kostas Papadimitriou | return super(UserResourceView, |
230 | 479c3051 | Ilias Tsitsimpis | self).queryset().filter(user=self.request.user_uniq) |
231 | eee0487e | Kostas Papadimitriou | |
232 | 49f50673 | Vangelis Koukis | |
233 | eee0487e | Kostas Papadimitriou | class UserCollectionView(CollectionView): |
234 | eee0487e | Kostas Papadimitriou | """
|
235 | eee0487e | Kostas Papadimitriou | Filter collection queryset for request user entries
|
236 | eee0487e | Kostas Papadimitriou | """
|
237 | eee0487e | Kostas Papadimitriou | def queryset(self): |
238 | 479c3051 | Ilias Tsitsimpis | return super(UserCollectionView, |
239 | 479c3051 | Ilias Tsitsimpis | self).queryset().filter(user=self.request.user_uniq) |
240 | eee0487e | Kostas Papadimitriou | |
241 | 69836bca | Kostas Papadimitriou | def POST(self, request, data, *args, **kwargs): |
242 | 69836bca | Kostas Papadimitriou | instance = self.model()
|
243 | 69836bca | Kostas Papadimitriou | self.update_instance(instance, data, self.exclude_fields) |
244 | 3ec71573 | Kostas Papadimitriou | instance.user = request.user_uniq |
245 | c72a830d | Kostas Papadimitriou | instance.full_clean() |
246 | 69836bca | Kostas Papadimitriou | instance.save() |
247 | 479c3051 | Ilias Tsitsimpis | return self.json_response( |
248 | 479c3051 | Ilias Tsitsimpis | self.instance_to_dict(instance, self.exclude_fields)) |