Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / userdata / rest.py @ 5facd191

History | View | Annotate | Download (8.4 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.template import RequestContext, loader
37 eee0487e Kostas Papadimitriou
from django.utils import simplejson as json
38 eee0487e Kostas Papadimitriou
from django.core import serializers
39 b896d198 Kostas Papadimitriou
from django.core.urlresolvers import reverse
40 eee0487e Kostas Papadimitriou
41 c72a830d Kostas Papadimitriou
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
42 c72a830d Kostas Papadimitriou
43 5facd191 Kostas Papadimitriou
from synnefo.lib.astakos import get_user
44 5facd191 Kostas Papadimitriou
from django.conf import settings
45 5facd191 Kostas Papadimitriou
46 eee0487e Kostas Papadimitriou
# base view class
47 eee0487e Kostas Papadimitriou
# https://github.com/bfirsh/django-class-based-views/blob/master/class_based_views/base.py
48 49f50673 Vangelis Koukis
49 49f50673 Vangelis Koukis
50 eee0487e Kostas Papadimitriou
class View(object):
51 eee0487e Kostas Papadimitriou
    """
52 eee0487e Kostas Papadimitriou
    Intentionally simple parent class for all views. Only implements
53 eee0487e Kostas Papadimitriou
    dispatch-by-method and simple sanity checking.
54 eee0487e Kostas Papadimitriou
    """
55 eee0487e Kostas Papadimitriou
56 eee0487e Kostas Papadimitriou
    method_names = ['GET', 'POST', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE']
57 eee0487e Kostas Papadimitriou
58 eee0487e Kostas Papadimitriou
    def __init__(self, *args, **kwargs):
59 eee0487e Kostas Papadimitriou
        """
60 eee0487e Kostas Papadimitriou
        Constructor. Called in the URLconf; can contain helpful extra
61 eee0487e Kostas Papadimitriou
        keyword arguments, and other things.
62 eee0487e Kostas Papadimitriou
        """
63 eee0487e Kostas Papadimitriou
        # Go through keyword arguments, and either save their values to our
64 eee0487e Kostas Papadimitriou
        # instance, or raise an error.
65 eee0487e Kostas Papadimitriou
        for key, value in kwargs.items():
66 eee0487e Kostas Papadimitriou
            if key in self.method_names:
67 49f50673 Vangelis Koukis
                raise TypeError(u"You tried to pass in the %s method name as a"
68 49f50673 Vangelis Koukis
                                u" keyword argument to %s(). Don't do that."
69 eee0487e Kostas Papadimitriou
                                % (key, self.__class__.__name__))
70 eee0487e Kostas Papadimitriou
            if hasattr(self, key):
71 eee0487e Kostas Papadimitriou
                setattr(self, key, value)
72 eee0487e Kostas Papadimitriou
            else:
73 eee0487e Kostas Papadimitriou
                raise TypeError(u"%s() received an invalid keyword %r" % (
74 eee0487e Kostas Papadimitriou
                    self.__class__.__name__,
75 eee0487e Kostas Papadimitriou
                    key,
76 eee0487e Kostas Papadimitriou
                ))
77 eee0487e Kostas Papadimitriou
78 eee0487e Kostas Papadimitriou
    @classmethod
79 eee0487e Kostas Papadimitriou
    def as_view(cls, *initargs, **initkwargs):
80 eee0487e Kostas Papadimitriou
        """
81 eee0487e Kostas Papadimitriou
        Main entry point for a request-response process.
82 eee0487e Kostas Papadimitriou
        """
83 eee0487e Kostas Papadimitriou
        def view(request, *args, **kwargs):
84 5facd191 Kostas Papadimitriou
            get_user(request, settings.ASTAKOS_URL)
85 eee0487e Kostas Papadimitriou
            self = cls(*initargs, **initkwargs)
86 eee0487e Kostas Papadimitriou
            return self.dispatch(request, *args, **kwargs)
87 eee0487e Kostas Papadimitriou
        return view
88 eee0487e Kostas Papadimitriou
89 eee0487e Kostas Papadimitriou
    def dispatch(self, request, *args, **kwargs):
90 eee0487e Kostas Papadimitriou
        # Try to dispatch to the right method for that; if it doesn't exist,
91 eee0487e Kostas Papadimitriou
        # raise a big error.
92 eee0487e Kostas Papadimitriou
        if hasattr(self, request.method.upper()):
93 eee0487e Kostas Papadimitriou
            self.request = request
94 eee0487e Kostas Papadimitriou
            self.args = args
95 eee0487e Kostas Papadimitriou
            self.kwargs = kwargs
96 eee0487e Kostas Papadimitriou
            data = request.raw_post_data
97 eee0487e Kostas Papadimitriou
98 eee0487e Kostas Papadimitriou
            if request.method.upper() in ['POST', 'PUT']:
99 eee0487e Kostas Papadimitriou
                # Expect json data
100 eee0487e Kostas Papadimitriou
                if request.META.get('CONTENT_TYPE').startswith('application/json'):
101 eee0487e Kostas Papadimitriou
                    try:
102 eee0487e Kostas Papadimitriou
                        data = json.loads(data)
103 eee0487e Kostas Papadimitriou
                    except ValueError:
104 eee0487e Kostas Papadimitriou
                        raise http.HttpResponseServerError('Invalid JSON data.')
105 eee0487e Kostas Papadimitriou
                else:
106 eee0487e Kostas Papadimitriou
                    raise http.HttpResponseServerError('Unsupported Content-Type.')
107 c72a830d Kostas Papadimitriou
            try:
108 c72a830d Kostas Papadimitriou
                return getattr(self, request.method.upper())(request, data, *args, **kwargs)
109 c72a830d Kostas Papadimitriou
            except ValidationError, e:
110 c72a830d Kostas Papadimitriou
                # specific response for validation errors
111 c72a830d Kostas Papadimitriou
                return http.HttpResponseServerError(json.dumps({'errors':
112 c72a830d Kostas Papadimitriou
                    e.message_dict, 'non_field_key':
113 c72a830d Kostas Papadimitriou
                    NON_FIELD_ERRORS }))
114 eee0487e Kostas Papadimitriou
115 eee0487e Kostas Papadimitriou
        else:
116 eee0487e Kostas Papadimitriou
            allowed_methods = [m for m in self.method_names if hasattr(self, m)]
117 eee0487e Kostas Papadimitriou
            return http.HttpResponseNotAllowed(allowed_methods)
118 eee0487e Kostas Papadimitriou
119 49f50673 Vangelis Koukis
120 b896d198 Kostas Papadimitriou
class JSONRestView(View):
121 b896d198 Kostas Papadimitriou
    """
122 b896d198 Kostas Papadimitriou
    Class that provides helpers to produce a json response
123 b896d198 Kostas Papadimitriou
    """
124 eee0487e Kostas Papadimitriou
125 b896d198 Kostas Papadimitriou
    url_name = None
126 b896d198 Kostas Papadimitriou
    def __init__(self, url_name, *args, **kwargs):
127 b896d198 Kostas Papadimitriou
        self.url_name = url_name
128 b896d198 Kostas Papadimitriou
        return super(JSONRestView, self).__init__(*args, **kwargs)
129 b896d198 Kostas Papadimitriou
130 69836bca Kostas Papadimitriou
    def update_instance(self, i, data, exclude_fields=[]):
131 b896d198 Kostas Papadimitriou
        update_keys = data.keys()
132 b896d198 Kostas Papadimitriou
        for field in i._meta.get_all_field_names():
133 69836bca Kostas Papadimitriou
            if field in update_keys and (field not in exclude_fields):
134 b896d198 Kostas Papadimitriou
                i.__setattr__(field, data[field])
135 b896d198 Kostas Papadimitriou
136 b896d198 Kostas Papadimitriou
        return i
137 b896d198 Kostas Papadimitriou
138 b896d198 Kostas Papadimitriou
    def instance_to_dict(self, i, exclude_fields=[]):
139 b896d198 Kostas Papadimitriou
        """
140 b896d198 Kostas Papadimitriou
        Convert model instance to python dict
141 b896d198 Kostas Papadimitriou
        """
142 b896d198 Kostas Papadimitriou
        d = {}
143 b896d198 Kostas Papadimitriou
        d['uri'] = reverse(self.url_name, kwargs={'id': i.pk})
144 b896d198 Kostas Papadimitriou
145 b896d198 Kostas Papadimitriou
        for field in i._meta.get_all_field_names():
146 b896d198 Kostas Papadimitriou
            if field in exclude_fields:
147 b896d198 Kostas Papadimitriou
                continue
148 b896d198 Kostas Papadimitriou
149 b896d198 Kostas Papadimitriou
            d[field] = i.__getattribute__(field)
150 b896d198 Kostas Papadimitriou
        return d
151 b896d198 Kostas Papadimitriou
152 b896d198 Kostas Papadimitriou
    def qs_to_dict_iter(self, qs, exclude_fields=[]):
153 b896d198 Kostas Papadimitriou
        """
154 b896d198 Kostas Papadimitriou
        Convert queryset to an iterator of model instances dicts
155 b896d198 Kostas Papadimitriou
        """
156 b896d198 Kostas Papadimitriou
        for i in qs:
157 b896d198 Kostas Papadimitriou
            yield self.instance_to_dict(i, exclude_fields)
158 b896d198 Kostas Papadimitriou
159 b896d198 Kostas Papadimitriou
    def json_response(self, data):
160 b896d198 Kostas Papadimitriou
        return http.HttpResponse(json.dumps(data), mimetype="application/json")
161 b896d198 Kostas Papadimitriou
162 49f50673 Vangelis Koukis
163 b896d198 Kostas Papadimitriou
class ResourceView(JSONRestView):
164 eee0487e Kostas Papadimitriou
    method_names = ['GET', 'POST', 'PUT', 'DELETE']
165 eee0487e Kostas Papadimitriou
166 eee0487e Kostas Papadimitriou
    model = None
167 eee0487e Kostas Papadimitriou
    exclude_fields = []
168 eee0487e Kostas Papadimitriou
169 eee0487e Kostas Papadimitriou
    def queryset(self):
170 eee0487e Kostas Papadimitriou
        return self.model.objects.all()
171 eee0487e Kostas Papadimitriou
172 eee0487e Kostas Papadimitriou
    def instance(self):
173 eee0487e Kostas Papadimitriou
        """
174 eee0487e Kostas Papadimitriou
        Retrieve selected instance based on url parameter
175 eee0487e Kostas Papadimitriou

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