blob: b17f135b43b14fa03fbcaa62c3fe21d76c23e5d9 [file] [log] [blame]
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -05001from rest_framework.decorators import api_view
2from rest_framework.response import Response
3from rest_framework.reverse import reverse
4from rest_framework import serializers
Sapan Bhatiadf2b49e2014-01-28 19:41:07 -05005from rest_framework import generics
Scott Bakerdcb9b5d2014-10-07 00:10:17 -07006from rest_framework import status
Scott Bakerc480fb22014-11-12 01:12:28 -08007from rest_framework.generics import GenericAPIView
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -05008from core.models import *
9from django.forms import widgets
Scott Baker46b58542014-08-11 17:26:12 -070010from rest_framework import filters
Scott Baker8ffd7d72014-11-10 15:58:58 -080011from django.conf.urls import patterns, url
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050012
13"""
Sapan Bhatia970beb52014-10-08 11:34:23 -040014 Schema of the generator object:
15 all: Set of all Model objects
16 all_if(regex): Set of Model objects that match regex
Scott Bakerc480fb22014-11-12 01:12:28 -080017
Sapan Bhatia970beb52014-10-08 11:34:23 -040018 Model object:
19 plural: English plural of object name
20 camel: CamelCase version of object name
21 refs: list of references to other Model objects
22 props: list of properties minus refs
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050023
Sapan Bhatia970beb52014-10-08 11:34:23 -040024 TODO: Deal with subnets
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050025"""
26
Scott Baker8ffd7d72014-11-10 15:58:58 -080027def get_REST_patterns():
28 return patterns('',
29 url(r'^plstackapi/$', api_root),
30 {% for object in generator.all %}
31 url(r'plstackapi/{{ object.rest_name }}/$', {{ object.camel }}List.as_view(), name='{{ object.singular }}-list'),
32 url(r'plstackapi/{{ object.rest_name }}/(?P<pk>[a-zA-Z0-9\-]+)/$', {{ object.camel }}Detail.as_view(), name ='{{ object.singular }}-detail'),
33 {% endfor %}
34 )
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050035
36@api_view(['GET'])
37def api_root(request, format=None):
38 return Response({
Sapan Bhatia970beb52014-10-08 11:34:23 -040039 {% for object in generator.all %}'{{ object.plural }}': reverse('{{ object }}-list', request=request, format=format),
40 {% endfor %}
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050041 })
42
43# Based on serializers.py
44
45{% for object in generator.all %}
46
47class {{ object.camel }}Serializer(serializers.HyperlinkedModelSerializer):
Sapan Bhatia970beb52014-10-08 11:34:23 -040048 id = serializers.Field()
49 {% for ref in object.refs %}
50 {% if ref.multi %}
51 {{ ref.plural }} = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='{{ ref }}-detail')
52 {% else %}
53 {{ ref }} = serializers.HyperlinkedRelatedField(read_only=True, view_name='{{ ref }}-detail')
54 {% endif %}
55 {% endfor %}
Scott Baker196ffef2014-11-21 11:32:24 -080056 humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
Scott Baker351148c2014-11-25 00:53:25 -080057 validators = serializers.SerializerMethodField("getValidators")
Scott Baker196ffef2014-11-21 11:32:24 -080058 def getHumanReadableName(self, obj):
59 return str(obj)
Scott Baker351148c2014-11-25 00:53:25 -080060 def getValidators(self, obj):
61 try:
62 return obj.getValidators()
63 except:
64 return None
Sapan Bhatia970beb52014-10-08 11:34:23 -040065 class Meta:
66 model = {{ object.camel }}
Scott Baker351148c2014-11-25 00:53:25 -080067 fields = ('humanReadableName', 'validators', {% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
Scott Baker9a270922014-07-03 18:01:30 -070068
69class {{ object.camel }}IdSerializer(serializers.ModelSerializer):
Sapan Bhatia970beb52014-10-08 11:34:23 -040070 id = serializers.Field()
71 {% for ref in object.refs %}
72 {% if ref.multi %}
73 {{ ref.plural }} = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='{{ ref }}-detail')
74 {% else %}
75 {{ ref }} = serializers.HyperlinkedRelatedField(read_only=True, view_name='{{ ref }}-detail')
76 {% endif %}
77 {% endfor %}
Scott Baker196ffef2014-11-21 11:32:24 -080078 humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
Scott Baker351148c2014-11-25 00:53:25 -080079 validators = serializers.SerializerMethodField("getValidators")
Scott Baker196ffef2014-11-21 11:32:24 -080080 def getHumanReadableName(self, obj):
81 return str(obj)
Scott Baker351148c2014-11-25 00:53:25 -080082 def getValidators(self, obj):
83 try:
84 return obj.getValidators()
85 except:
86 return None
Sapan Bhatia970beb52014-10-08 11:34:23 -040087 class Meta:
88 model = {{ object.camel }}
Scott Baker351148c2014-11-25 00:53:25 -080089 fields = ('humanReadableName', 'validators', {% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
Scott Baker9a270922014-07-03 18:01:30 -070090
91
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050092{% endfor %}
93
94serializerLookUp = {
95{% for object in generator.all %}
96 {{ object.camel }}: {{ object.camel }}Serializer,
97{% endfor %}
98 None: None,
99 }
100
Scott Bakerdcb9b5d2014-10-07 00:10:17 -0700101class PlanetStackRetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
102
103 # To handle fine-grained field permissions, we have to check can_update
104 # the object has been updated but before it has been saved.
105
106 def update(self, request, *args, **kwargs):
107 partial = kwargs.pop('partial', False)
108 self.object = self.get_object_or_none()
109
110 serializer = self.get_serializer(self.object, data=request.DATA,
111 files=request.FILES, partial=partial)
112
113 if not serializer.is_valid():
Scott Baker351148c2014-11-25 00:53:25 -0800114 response = {"error": "validation",
115 "specific_error": "not serializer.is_valid()",
116 "reasons": serializer.errors}
117 return Response(response, status=status.HTTP_400_BAD_REQUEST)
Scott Bakerdcb9b5d2014-10-07 00:10:17 -0700118
119 try:
120 self.pre_save(serializer.object)
121 except ValidationError as err:
122 # full_clean on model instance may be called in pre_save,
123 # so we have to handle eventual errors.
Scott Baker351148c2014-11-25 00:53:25 -0800124 response = {"error": "validation",
125 "specific_error": "ValidationError in pre_save",
126 "reasons": err.message_dict}
127 return Response(response, status=status.HTTP_400_BAD_REQUEST)
Scott Bakerdcb9b5d2014-10-07 00:10:17 -0700128
129 if serializer.object is not None:
130 if not serializer.object.can_update(request.user):
131 return Response(status=status.HTTP_400_BAD_REQUEST)
132
133 if self.object is None:
134 self.object = serializer.save(force_insert=True)
135 self.post_save(self.object, created=True)
136 return Response(serializer.data, status=status.HTTP_201_CREATED)
137
138 self.object = serializer.save(force_update=True)
139 self.post_save(self.object, created=False)
140 return Response(serializer.data, status=status.HTTP_200_OK)
141
142 def destroy(self, request, *args, **kwargs):
143 obj = self.get_object()
144 if obj.can_update(request.user):
Scott Bakerde12f092014-10-09 11:09:15 -0700145 return super(generics.RetrieveUpdateDestroyAPIView, self).destroy(request, *args, **kwargs)
Scott Bakerdcb9b5d2014-10-07 00:10:17 -0700146 else:
147 return Response(status=status.HTTP_400_BAD_REQUEST)
148
149
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500150# Based on core/views/*.py
151{% for object in generator.all %}
152
153class {{ object.camel }}List(generics.ListCreateAPIView):
Sapan Bhatiadf2b49e2014-01-28 19:41:07 -0500154 queryset = {{ object.camel }}.objects.select_related().all()
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500155 serializer_class = {{ object.camel }}Serializer
Scott Baker9a270922014-07-03 18:01:30 -0700156 id_serializer_class = {{ object.camel }}IdSerializer
Scott Baker46b58542014-08-11 17:26:12 -0700157 filter_backends = (filters.DjangoFilterBackend,)
158 filter_fields = ({% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
Scott Baker9a270922014-07-03 18:01:30 -0700159
160 def get_serializer_class(self):
161 no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
162 if (no_hyperlinks):
163 return self.id_serializer_class
164 else:
165 return self.serializer_class
166
Tony Mackeb8eb312014-02-04 20:50:39 -0500167 def get_queryset(self):
Tony Mack8f04ee32014-02-05 10:27:39 -0500168 return {{ object.camel }}.select_by_user(self.request.user)
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500169
Tony Mack9525eba2014-02-05 10:57:21 -0500170 def create(self, request, *args, **kwargs):
Scott Bakerc480fb22014-11-12 01:12:28 -0800171 serializer = self.get_serializer(data=request.DATA, files=request.FILES)
172 if not (serializer.is_valid()):
Scott Baker351148c2014-11-25 00:53:25 -0800173 response = {"error": "validation",
174 "specific_error": "not serializer.is_valid()",
175 "reasons": serializer.errors}
176 return Response(response, status=status.HTTP_400_BAD_REQUEST)
Scott Bakerc480fb22014-11-12 01:12:28 -0800177 obj = serializer.object
Scott Bakerde12f092014-10-09 11:09:15 -0700178 obj.caller = request.user
179 if obj.can_update(request.user):
180 return super({{ object.camel }}List, self).create(request, *args, **kwargs)
181 else:
182 raise Exception("failed obj.can_update")
183
Sapan Bhatia970beb52014-10-08 11:34:23 -0400184 ret = super({{ object.camel }}List, self).create(request, *args, **kwargs)
185 if (ret.status_code%100 != 200):
186 raise Exception(ret.data)
Scott Bakerde12f092014-10-09 11:09:15 -0700187
188 return ret
189
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500190
Scott Bakerdcb9b5d2014-10-07 00:10:17 -0700191class {{ object.camel }}Detail(PlanetStackRetrieveUpdateDestroyAPIView):
Sapan Bhatiadf2b49e2014-01-28 19:41:07 -0500192 queryset = {{ object.camel }}.objects.select_related().all()
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500193 serializer_class = {{ object.camel }}Serializer
Scott Baker46b58542014-08-11 17:26:12 -0700194 id_serializer_class = {{ object.camel }}IdSerializer
195
196 def get_serializer_class(self):
197 no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
198 if (no_hyperlinks):
199 return self.id_serializer_class
200 else:
201 return self.serializer_class
Tony Mackeb8eb312014-02-04 20:50:39 -0500202
203 def get_queryset(self):
Tony Mack9525eba2014-02-05 10:57:21 -0500204 return {{ object.camel }}.select_by_user(self.request.user)
205
Scott Bakerdcb9b5d2014-10-07 00:10:17 -0700206 # update() is handled by PlanetStackRetrieveUpdateDestroyAPIView
Tony Mack9525eba2014-02-05 10:57:21 -0500207
Scott Bakerdcb9b5d2014-10-07 00:10:17 -0700208 # destroy() is handled by PlanetStackRetrieveUpdateDestroyAPIView
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500209
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500210{% endfor %}