Statistics
| Branch: | Tag: | Revision:

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

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

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