root / snf-cyclades-app / synnefo / userdata / rest.py @ 35ac0244
History | View | Annotate | Download (8.9 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 | 02990794 | Kostas Papadimitriou | import logging |
36 | 02990794 | Kostas Papadimitriou | |
37 | eee0487e | Kostas Papadimitriou | from django import http |
38 | eee0487e | Kostas Papadimitriou | from django.utils import simplejson as json |
39 | b896d198 | Kostas Papadimitriou | from django.core.urlresolvers import reverse |
40 | 3ec71573 | Kostas Papadimitriou | from django.http import HttpResponse |
41 | eee0487e | Kostas Papadimitriou | |
42 | c72a830d | Kostas Papadimitriou | from django.core.exceptions import ValidationError, NON_FIELD_ERRORS |
43 | c72a830d | Kostas Papadimitriou | |
44 | 04a1b675 | Christos Stavrakakis | from snf_django.lib.astakos import get_user |
45 | 5facd191 | Kostas Papadimitriou | from django.conf import settings |
46 | 5facd191 | Kostas Papadimitriou | |
47 | 49f50673 | Vangelis Koukis | |
48 | eee0487e | Kostas Papadimitriou | class View(object): |
49 | eee0487e | Kostas Papadimitriou | """
|
50 | eee0487e | Kostas Papadimitriou | Intentionally simple parent class for all views. Only implements
|
51 | eee0487e | Kostas Papadimitriou | dispatch-by-method and simple sanity checking.
|
52 | eee0487e | Kostas Papadimitriou | """
|
53 | eee0487e | Kostas Papadimitriou | |
54 | eee0487e | Kostas Papadimitriou | method_names = ['GET', 'POST', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE'] |
55 | eee0487e | Kostas Papadimitriou | |
56 | eee0487e | Kostas Papadimitriou | def __init__(self, *args, **kwargs): |
57 | eee0487e | Kostas Papadimitriou | """
|
58 | eee0487e | Kostas Papadimitriou | Constructor. Called in the URLconf; can contain helpful extra
|
59 | eee0487e | Kostas Papadimitriou | keyword arguments, and other things.
|
60 | eee0487e | Kostas Papadimitriou | """
|
61 | 02990794 | Kostas Papadimitriou | self.logger = logging.getLogger(self.model._meta.db_table) |
62 | eee0487e | Kostas Papadimitriou | # Go through keyword arguments, and either save their values to our
|
63 | eee0487e | Kostas Papadimitriou | # instance, or raise an error.
|
64 | eee0487e | Kostas Papadimitriou | for key, value in kwargs.items(): |
65 | eee0487e | Kostas Papadimitriou | if key in self.method_names: |
66 | 49f50673 | Vangelis Koukis | raise TypeError(u"You tried to pass in the %s method name as a" |
67 | 49f50673 | Vangelis Koukis | u" keyword argument to %s(). Don't do that."
|
68 | eee0487e | Kostas Papadimitriou | % (key, self.__class__.__name__))
|
69 | eee0487e | Kostas Papadimitriou | if hasattr(self, key): |
70 | eee0487e | Kostas Papadimitriou | setattr(self, key, value) |
71 | eee0487e | Kostas Papadimitriou | else:
|
72 | eee0487e | Kostas Papadimitriou | raise TypeError(u"%s() received an invalid keyword %r" % ( |
73 | eee0487e | Kostas Papadimitriou | self.__class__.__name__,
|
74 | eee0487e | Kostas Papadimitriou | key, |
75 | eee0487e | Kostas Papadimitriou | )) |
76 | eee0487e | Kostas Papadimitriou | |
77 | eee0487e | Kostas Papadimitriou | @classmethod
|
78 | eee0487e | Kostas Papadimitriou | def as_view(cls, *initargs, **initkwargs): |
79 | eee0487e | Kostas Papadimitriou | """
|
80 | eee0487e | Kostas Papadimitriou | Main entry point for a request-response process.
|
81 | eee0487e | Kostas Papadimitriou | """
|
82 | eee0487e | Kostas Papadimitriou | def view(request, *args, **kwargs): |
83 | e407f159 | Ilias Tsitsimpis | get_user(request, settings.ASTAKOS_AUTH_URL) |
84 | 3ec71573 | Kostas Papadimitriou | if not request.user_uniq: |
85 | 3ec71573 | Kostas Papadimitriou | return HttpResponse(status=401) |
86 | eee0487e | Kostas Papadimitriou | self = cls(*initargs, **initkwargs)
|
87 | eee0487e | Kostas Papadimitriou | return self.dispatch(request, *args, **kwargs) |
88 | eee0487e | Kostas Papadimitriou | return view
|
89 | eee0487e | Kostas Papadimitriou | |
90 | eee0487e | Kostas Papadimitriou | def dispatch(self, request, *args, **kwargs): |
91 | eee0487e | Kostas Papadimitriou | # Try to dispatch to the right method for that; if it doesn't exist,
|
92 | eee0487e | Kostas Papadimitriou | # raise a big error.
|
93 | eee0487e | Kostas Papadimitriou | if hasattr(self, request.method.upper()): |
94 | eee0487e | Kostas Papadimitriou | self.request = request
|
95 | eee0487e | Kostas Papadimitriou | self.args = args
|
96 | eee0487e | Kostas Papadimitriou | self.kwargs = kwargs
|
97 | b694875c | Christos Stavrakakis | data = request.body |
98 | eee0487e | Kostas Papadimitriou | |
99 | eee0487e | Kostas Papadimitriou | if request.method.upper() in ['POST', 'PUT']: |
100 | eee0487e | Kostas Papadimitriou | # Expect json data
|
101 | 479c3051 | Ilias Tsitsimpis | if request.META.get('CONTENT_TYPE').startswith( |
102 | 479c3051 | Ilias Tsitsimpis | 'application/json'):
|
103 | eee0487e | Kostas Papadimitriou | try:
|
104 | eee0487e | Kostas Papadimitriou | data = json.loads(data) |
105 | eee0487e | Kostas Papadimitriou | except ValueError: |
106 | 479c3051 | Ilias Tsitsimpis | return \
|
107 | 479c3051 | Ilias Tsitsimpis | http.HttpResponseServerError('Invalid JSON data.')
|
108 | eee0487e | Kostas Papadimitriou | else:
|
109 | 479c3051 | Ilias Tsitsimpis | return http.HttpResponseServerError(
|
110 | 479c3051 | Ilias Tsitsimpis | 'Unsupported Content-Type.')
|
111 | c72a830d | Kostas Papadimitriou | try:
|
112 | 479c3051 | Ilias Tsitsimpis | return getattr(self, request.method.upper())( |
113 | 479c3051 | Ilias Tsitsimpis | request, data, *args, **kwargs) |
114 | c72a830d | Kostas Papadimitriou | except ValidationError, e:
|
115 | c72a830d | Kostas Papadimitriou | # specific response for validation errors
|
116 | bf18a788 | Kostas Papadimitriou | return http.HttpResponse(
|
117 | 479c3051 | Ilias Tsitsimpis | json.dumps({'errors': e.message_dict,
|
118 | bf18a788 | Kostas Papadimitriou | 'non_field_key': NON_FIELD_ERRORS}),
|
119 | bf18a788 | Kostas Papadimitriou | status=422, content_type="application/json") |
120 | eee0487e | Kostas Papadimitriou | |
121 | eee0487e | Kostas Papadimitriou | else:
|
122 | e8e50dcc | Ilias Tsitsimpis | allowed_methods = \ |
123 | e8e50dcc | Ilias Tsitsimpis | [m for m in self.method_names if hasattr(self, m)] |
124 | eee0487e | Kostas Papadimitriou | return http.HttpResponseNotAllowed(allowed_methods)
|
125 | eee0487e | Kostas Papadimitriou | |
126 | 49f50673 | Vangelis Koukis | |
127 | b896d198 | Kostas Papadimitriou | class JSONRestView(View): |
128 | b896d198 | Kostas Papadimitriou | """
|
129 | b896d198 | Kostas Papadimitriou | Class that provides helpers to produce a json response
|
130 | b896d198 | Kostas Papadimitriou | """
|
131 | eee0487e | Kostas Papadimitriou | |
132 | b896d198 | Kostas Papadimitriou | url_name = None
|
133 | 479c3051 | Ilias Tsitsimpis | |
134 | b896d198 | Kostas Papadimitriou | def __init__(self, url_name, *args, **kwargs): |
135 | b896d198 | Kostas Papadimitriou | self.url_name = url_name
|
136 | b896d198 | Kostas Papadimitriou | return super(JSONRestView, self).__init__(*args, **kwargs) |
137 | b896d198 | Kostas Papadimitriou | |
138 | 69836bca | Kostas Papadimitriou | def update_instance(self, i, data, exclude_fields=[]): |
139 | b896d198 | Kostas Papadimitriou | update_keys = data.keys() |
140 | b896d198 | Kostas Papadimitriou | for field in i._meta.get_all_field_names(): |
141 | 69836bca | Kostas Papadimitriou | if field in update_keys and (field not in exclude_fields): |
142 | b896d198 | Kostas Papadimitriou | i.__setattr__(field, data[field]) |
143 | b896d198 | Kostas Papadimitriou | |
144 | b896d198 | Kostas Papadimitriou | return i
|
145 | b896d198 | Kostas Papadimitriou | |
146 | b896d198 | Kostas Papadimitriou | def instance_to_dict(self, i, exclude_fields=[]): |
147 | b896d198 | Kostas Papadimitriou | """
|
148 | b896d198 | Kostas Papadimitriou | Convert model instance to python dict
|
149 | b896d198 | Kostas Papadimitriou | """
|
150 | b896d198 | Kostas Papadimitriou | d = {} |
151 | b896d198 | Kostas Papadimitriou | d['uri'] = reverse(self.url_name, kwargs={'id': i.pk}) |
152 | b896d198 | Kostas Papadimitriou | |
153 | b896d198 | Kostas Papadimitriou | for field in i._meta.get_all_field_names(): |
154 | b896d198 | Kostas Papadimitriou | if field in exclude_fields: |
155 | b896d198 | Kostas Papadimitriou | continue
|
156 | b896d198 | Kostas Papadimitriou | |
157 | b896d198 | Kostas Papadimitriou | d[field] = i.__getattribute__(field) |
158 | b896d198 | Kostas Papadimitriou | return d
|
159 | b896d198 | Kostas Papadimitriou | |
160 | b896d198 | Kostas Papadimitriou | def qs_to_dict_iter(self, qs, exclude_fields=[]): |
161 | b896d198 | Kostas Papadimitriou | """
|
162 | b896d198 | Kostas Papadimitriou | Convert queryset to an iterator of model instances dicts
|
163 | b896d198 | Kostas Papadimitriou | """
|
164 | b896d198 | Kostas Papadimitriou | for i in qs: |
165 | b896d198 | Kostas Papadimitriou | yield self.instance_to_dict(i, exclude_fields) |
166 | b896d198 | Kostas Papadimitriou | |
167 | b896d198 | Kostas Papadimitriou | def json_response(self, data): |
168 | b896d198 | Kostas Papadimitriou | return http.HttpResponse(json.dumps(data), mimetype="application/json") |
169 | b896d198 | Kostas Papadimitriou | |
170 | 49f50673 | Vangelis Koukis | |
171 | b896d198 | Kostas Papadimitriou | class ResourceView(JSONRestView): |
172 | eee0487e | Kostas Papadimitriou | method_names = ['GET', 'POST', 'PUT', 'DELETE'] |
173 | eee0487e | Kostas Papadimitriou | |
174 | eee0487e | Kostas Papadimitriou | model = None
|
175 | eee0487e | Kostas Papadimitriou | exclude_fields = [] |
176 | eee0487e | Kostas Papadimitriou | |
177 | eee0487e | Kostas Papadimitriou | def queryset(self): |
178 | eee0487e | Kostas Papadimitriou | return self.model.objects.all() |
179 | eee0487e | Kostas Papadimitriou | |
180 | eee0487e | Kostas Papadimitriou | def instance(self): |
181 | eee0487e | Kostas Papadimitriou | """
|
182 | eee0487e | Kostas Papadimitriou | Retrieve selected instance based on url parameter
|
183 | eee0487e | Kostas Papadimitriou |
|
184 | eee0487e | Kostas Papadimitriou | id parameter should be set in urlpatterns expression
|
185 | eee0487e | Kostas Papadimitriou | """
|
186 | eee0487e | Kostas Papadimitriou | try:
|
187 | eee0487e | Kostas Papadimitriou | return self.queryset().get(pk=self.kwargs.get("id")) |
188 | eee0487e | Kostas Papadimitriou | except self.model.DoesNotExist: |
189 | eee0487e | Kostas Papadimitriou | raise http.Http404
|
190 | eee0487e | Kostas Papadimitriou | |
191 | eee0487e | Kostas Papadimitriou | def GET(self, request, data, *args, **kwargs): |
192 | 479c3051 | Ilias Tsitsimpis | return self.json_response( |
193 | 479c3051 | Ilias Tsitsimpis | self.instance_to_dict(self.instance(), self.exclude_fields)) |
194 | eee0487e | Kostas Papadimitriou | |
195 | 69836bca | Kostas Papadimitriou | def PUT(self, request, data, *args, **kwargs): |
196 | b896d198 | Kostas Papadimitriou | instance = self.instance()
|
197 | 69836bca | Kostas Papadimitriou | self.update_instance(instance, data, self.exclude_fields) |
198 | c72a830d | Kostas Papadimitriou | instance.full_clean() |
199 | b896d198 | Kostas Papadimitriou | instance.save() |
200 | 02990794 | Kostas Papadimitriou | self.logger.info("Instance [%d] updated", instance.pk) |
201 | b896d198 | Kostas Papadimitriou | return self.GET(request, data, *args, **kwargs) |
202 | eee0487e | Kostas Papadimitriou | |
203 | eee0487e | Kostas Papadimitriou | def DELETE(self, request, data, *args, **kwargs): |
204 | 02990794 | Kostas Papadimitriou | instance = self.instance()
|
205 | 02990794 | Kostas Papadimitriou | pk = instance.pk |
206 | 02990794 | Kostas Papadimitriou | instance.delete() |
207 | 02990794 | Kostas Papadimitriou | self.logger.info("Instance [%d] removed", pk) |
208 | b896d198 | Kostas Papadimitriou | return self.json_response("") |
209 | eee0487e | Kostas Papadimitriou | |
210 | eee0487e | Kostas Papadimitriou | |
211 | b896d198 | Kostas Papadimitriou | class CollectionView(JSONRestView): |
212 | b896d198 | Kostas Papadimitriou | method_names = ['GET', 'POST'] |
213 | eee0487e | Kostas Papadimitriou | |
214 | eee0487e | Kostas Papadimitriou | model = None
|
215 | eee0487e | Kostas Papadimitriou | exclude_fields = [] |
216 | eee0487e | Kostas Papadimitriou | |
217 | eee0487e | Kostas Papadimitriou | def queryset(self): |
218 | eee0487e | Kostas Papadimitriou | return self.model.objects.all() |
219 | eee0487e | Kostas Papadimitriou | |
220 | eee0487e | Kostas Papadimitriou | def GET(self, request, data, *args, **kwargs): |
221 | 479c3051 | Ilias Tsitsimpis | return self.json_response( |
222 | 479c3051 | Ilias Tsitsimpis | list(self.qs_to_dict_iter(self.queryset(), self.exclude_fields))) |
223 | eee0487e | Kostas Papadimitriou | |
224 | b896d198 | Kostas Papadimitriou | def POST(self, request, data, *args, **kwargs): |
225 | 69836bca | Kostas Papadimitriou | instance = self.model()
|
226 | 69836bca | Kostas Papadimitriou | self.update_instance(instance, data, self.exclude_fields) |
227 | c72a830d | Kostas Papadimitriou | instance.full_clean() |
228 | 69836bca | Kostas Papadimitriou | instance.save() |
229 | 02990794 | Kostas Papadimitriou | self.logger.info("Instance [%d] created", instance.pk) |
230 | 479c3051 | Ilias Tsitsimpis | return self.json_response( |
231 | 479c3051 | Ilias Tsitsimpis | self.instance_to_dict(instance, self.exclude_fields)) |
232 | eee0487e | Kostas Papadimitriou | |
233 | 49f50673 | Vangelis Koukis | |
234 | eee0487e | Kostas Papadimitriou | class UserResourceView(ResourceView): |
235 | eee0487e | Kostas Papadimitriou | """
|
236 | eee0487e | Kostas Papadimitriou | Filter resource queryset for request user entries
|
237 | eee0487e | Kostas Papadimitriou | """
|
238 | eee0487e | Kostas Papadimitriou | def queryset(self): |
239 | eee0487e | Kostas Papadimitriou | return super(UserResourceView, |
240 | 479c3051 | Ilias Tsitsimpis | self).queryset().filter(user=self.request.user_uniq) |
241 | eee0487e | Kostas Papadimitriou | |
242 | 49f50673 | Vangelis Koukis | |
243 | eee0487e | Kostas Papadimitriou | class UserCollectionView(CollectionView): |
244 | eee0487e | Kostas Papadimitriou | """
|
245 | eee0487e | Kostas Papadimitriou | Filter collection queryset for request user entries
|
246 | eee0487e | Kostas Papadimitriou | """
|
247 | eee0487e | Kostas Papadimitriou | def queryset(self): |
248 | 479c3051 | Ilias Tsitsimpis | return super(UserCollectionView, |
249 | 479c3051 | Ilias Tsitsimpis | self).queryset().filter(user=self.request.user_uniq) |
250 | eee0487e | Kostas Papadimitriou | |
251 | 69836bca | Kostas Papadimitriou | def POST(self, request, data, *args, **kwargs): |
252 | 69836bca | Kostas Papadimitriou | instance = self.model()
|
253 | 69836bca | Kostas Papadimitriou | self.update_instance(instance, data, self.exclude_fields) |
254 | 3ec71573 | Kostas Papadimitriou | instance.user = request.user_uniq |
255 | c72a830d | Kostas Papadimitriou | instance.full_clean() |
256 | 69836bca | Kostas Papadimitriou | instance.save() |
257 | 02990794 | Kostas Papadimitriou | self.logger.info("Instance [%d] created", instance.pk) |
258 | 479c3051 | Ilias Tsitsimpis | return self.json_response( |
259 | 479c3051 | Ilias Tsitsimpis | self.instance_to_dict(instance, self.exclude_fields)) |