Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / userdata / rest.py @ bf18a788

History | View | Annotate | Download (8.6 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 e407f159 Ilias Tsitsimpis
            get_user(request, settings.ASTAKOS_AUTH_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 b694875c Christos Stavrakakis
            data = request.body
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 bf18a788 Kostas Papadimitriou
                return http.HttpResponse(
114 479c3051 Ilias Tsitsimpis
                    json.dumps({'errors': e.message_dict,
115 bf18a788 Kostas Papadimitriou
                                'non_field_key': NON_FIELD_ERRORS}),
116 bf18a788 Kostas Papadimitriou
                status=422, content_type="application/json")
117 eee0487e Kostas Papadimitriou
118 eee0487e Kostas Papadimitriou
        else:
119 e8e50dcc Ilias Tsitsimpis
            allowed_methods = \
120 e8e50dcc Ilias Tsitsimpis
                [m for m in self.method_names if hasattr(self, m)]
121 eee0487e Kostas Papadimitriou
            return http.HttpResponseNotAllowed(allowed_methods)
122 eee0487e Kostas Papadimitriou
123 49f50673 Vangelis Koukis
124 b896d198 Kostas Papadimitriou
class JSONRestView(View):
125 b896d198 Kostas Papadimitriou
    """
126 b896d198 Kostas Papadimitriou
    Class that provides helpers to produce a json response
127 b896d198 Kostas Papadimitriou
    """
128 eee0487e Kostas Papadimitriou
129 b896d198 Kostas Papadimitriou
    url_name = None
130 479c3051 Ilias Tsitsimpis
131 b896d198 Kostas Papadimitriou
    def __init__(self, url_name, *args, **kwargs):
132 b896d198 Kostas Papadimitriou
        self.url_name = url_name
133 b896d198 Kostas Papadimitriou
        return super(JSONRestView, self).__init__(*args, **kwargs)
134 b896d198 Kostas Papadimitriou
135 69836bca Kostas Papadimitriou
    def update_instance(self, i, data, exclude_fields=[]):
136 b896d198 Kostas Papadimitriou
        update_keys = data.keys()
137 b896d198 Kostas Papadimitriou
        for field in i._meta.get_all_field_names():
138 69836bca Kostas Papadimitriou
            if field in update_keys and (field not in exclude_fields):
139 b896d198 Kostas Papadimitriou
                i.__setattr__(field, data[field])
140 b896d198 Kostas Papadimitriou
141 b896d198 Kostas Papadimitriou
        return i
142 b896d198 Kostas Papadimitriou
143 b896d198 Kostas Papadimitriou
    def instance_to_dict(self, i, exclude_fields=[]):
144 b896d198 Kostas Papadimitriou
        """
145 b896d198 Kostas Papadimitriou
        Convert model instance to python dict
146 b896d198 Kostas Papadimitriou
        """
147 b896d198 Kostas Papadimitriou
        d = {}
148 b896d198 Kostas Papadimitriou
        d['uri'] = reverse(self.url_name, kwargs={'id': i.pk})
149 b896d198 Kostas Papadimitriou
150 b896d198 Kostas Papadimitriou
        for field in i._meta.get_all_field_names():
151 b896d198 Kostas Papadimitriou
            if field in exclude_fields:
152 b896d198 Kostas Papadimitriou
                continue
153 b896d198 Kostas Papadimitriou
154 b896d198 Kostas Papadimitriou
            d[field] = i.__getattribute__(field)
155 b896d198 Kostas Papadimitriou
        return d
156 b896d198 Kostas Papadimitriou
157 b896d198 Kostas Papadimitriou
    def qs_to_dict_iter(self, qs, exclude_fields=[]):
158 b896d198 Kostas Papadimitriou
        """
159 b896d198 Kostas Papadimitriou
        Convert queryset to an iterator of model instances dicts
160 b896d198 Kostas Papadimitriou
        """
161 b896d198 Kostas Papadimitriou
        for i in qs:
162 b896d198 Kostas Papadimitriou
            yield self.instance_to_dict(i, exclude_fields)
163 b896d198 Kostas Papadimitriou
164 b896d198 Kostas Papadimitriou
    def json_response(self, data):
165 b896d198 Kostas Papadimitriou
        return http.HttpResponse(json.dumps(data), mimetype="application/json")
166 b896d198 Kostas Papadimitriou
167 49f50673 Vangelis Koukis
168 b896d198 Kostas Papadimitriou
class ResourceView(JSONRestView):
169 eee0487e Kostas Papadimitriou
    method_names = ['GET', 'POST', 'PUT', 'DELETE']
170 eee0487e Kostas Papadimitriou
171 eee0487e Kostas Papadimitriou
    model = None
172 eee0487e Kostas Papadimitriou
    exclude_fields = []
173 eee0487e Kostas Papadimitriou
174 eee0487e Kostas Papadimitriou
    def queryset(self):
175 eee0487e Kostas Papadimitriou
        return self.model.objects.all()
176 eee0487e Kostas Papadimitriou
177 eee0487e Kostas Papadimitriou
    def instance(self):
178 eee0487e Kostas Papadimitriou
        """
179 eee0487e Kostas Papadimitriou
        Retrieve selected instance based on url parameter
180 eee0487e Kostas Papadimitriou

181 eee0487e Kostas Papadimitriou
        id parameter should be set in urlpatterns expression
182 eee0487e Kostas Papadimitriou
        """
183 eee0487e Kostas Papadimitriou
        try:
184 eee0487e Kostas Papadimitriou
            return self.queryset().get(pk=self.kwargs.get("id"))
185 eee0487e Kostas Papadimitriou
        except self.model.DoesNotExist:
186 eee0487e Kostas Papadimitriou
            raise http.Http404
187 eee0487e Kostas Papadimitriou
188 eee0487e Kostas Papadimitriou
    def GET(self, request, data, *args, **kwargs):
189 479c3051 Ilias Tsitsimpis
        return self.json_response(
190 479c3051 Ilias Tsitsimpis
            self.instance_to_dict(self.instance(), self.exclude_fields))
191 eee0487e Kostas Papadimitriou
192 69836bca Kostas Papadimitriou
    def PUT(self, request, data, *args, **kwargs):
193 b896d198 Kostas Papadimitriou
        instance = self.instance()
194 69836bca Kostas Papadimitriou
        self.update_instance(instance, data, self.exclude_fields)
195 c72a830d Kostas Papadimitriou
        instance.full_clean()
196 b896d198 Kostas Papadimitriou
        instance.save()
197 b896d198 Kostas Papadimitriou
        return self.GET(request, data, *args, **kwargs)
198 eee0487e Kostas Papadimitriou
199 eee0487e Kostas Papadimitriou
    def DELETE(self, request, data, *args, **kwargs):
200 eee0487e Kostas Papadimitriou
        self.instance().delete()
201 b896d198 Kostas Papadimitriou
        return self.json_response("")
202 eee0487e Kostas Papadimitriou
203 eee0487e Kostas Papadimitriou
204 b896d198 Kostas Papadimitriou
class CollectionView(JSONRestView):
205 b896d198 Kostas Papadimitriou
    method_names = ['GET', 'POST']
206 eee0487e Kostas Papadimitriou
207 eee0487e Kostas Papadimitriou
    model = None
208 eee0487e Kostas Papadimitriou
    exclude_fields = []
209 eee0487e Kostas Papadimitriou
210 eee0487e Kostas Papadimitriou
    def queryset(self):
211 eee0487e Kostas Papadimitriou
        return self.model.objects.all()
212 eee0487e Kostas Papadimitriou
213 eee0487e Kostas Papadimitriou
    def GET(self, request, data, *args, **kwargs):
214 479c3051 Ilias Tsitsimpis
        return self.json_response(
215 479c3051 Ilias Tsitsimpis
            list(self.qs_to_dict_iter(self.queryset(), self.exclude_fields)))
216 eee0487e Kostas Papadimitriou
217 b896d198 Kostas Papadimitriou
    def POST(self, request, data, *args, **kwargs):
218 69836bca Kostas Papadimitriou
        instance = self.model()
219 69836bca Kostas Papadimitriou
        self.update_instance(instance, data, self.exclude_fields)
220 c72a830d Kostas Papadimitriou
        instance.full_clean()
221 69836bca Kostas Papadimitriou
        instance.save()
222 479c3051 Ilias Tsitsimpis
        return self.json_response(
223 479c3051 Ilias Tsitsimpis
            self.instance_to_dict(instance, self.exclude_fields))
224 eee0487e Kostas Papadimitriou
225 49f50673 Vangelis Koukis
226 eee0487e Kostas Papadimitriou
class UserResourceView(ResourceView):
227 eee0487e Kostas Papadimitriou
    """
228 eee0487e Kostas Papadimitriou
    Filter resource queryset for request user entries
229 eee0487e Kostas Papadimitriou
    """
230 eee0487e Kostas Papadimitriou
    def queryset(self):
231 eee0487e Kostas Papadimitriou
        return super(UserResourceView,
232 479c3051 Ilias Tsitsimpis
                     self).queryset().filter(user=self.request.user_uniq)
233 eee0487e Kostas Papadimitriou
234 49f50673 Vangelis Koukis
235 eee0487e Kostas Papadimitriou
class UserCollectionView(CollectionView):
236 eee0487e Kostas Papadimitriou
    """
237 eee0487e Kostas Papadimitriou
    Filter collection queryset for request user entries
238 eee0487e Kostas Papadimitriou
    """
239 eee0487e Kostas Papadimitriou
    def queryset(self):
240 479c3051 Ilias Tsitsimpis
        return super(UserCollectionView,
241 479c3051 Ilias Tsitsimpis
                     self).queryset().filter(user=self.request.user_uniq)
242 eee0487e Kostas Papadimitriou
243 69836bca Kostas Papadimitriou
    def POST(self, request, data, *args, **kwargs):
244 69836bca Kostas Papadimitriou
        instance = self.model()
245 69836bca Kostas Papadimitriou
        self.update_instance(instance, data, self.exclude_fields)
246 3ec71573 Kostas Papadimitriou
        instance.user = request.user_uniq
247 c72a830d Kostas Papadimitriou
        instance.full_clean()
248 69836bca Kostas Papadimitriou
        instance.save()
249 479c3051 Ilias Tsitsimpis
        return self.json_response(
250 479c3051 Ilias Tsitsimpis
            self.instance_to_dict(instance, self.exclude_fields))