Statistics
| Branch: | Tag: | Revision:

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