Statistics
| Branch: | Tag: | Revision:

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

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 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 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))