blob: 5302c62e59060a74f0a60ea4a600c608b0ea3f83 [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 Baker89a7b7c2014-10-07 00:10:17 -07006from rest_framework import status
Scott Baker6ad17902014-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 Bakerf556b922014-11-10 15:58:58 -080011from django.conf.urls import patterns, url
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050012
13"""
Sapan Bhatiaf69dd5c2014-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 Baker6ad17902014-11-12 01:12:28 -080017
Sapan Bhatiaf69dd5c2014-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 Bhatiaf69dd5c2014-10-08 11:34:23 -040024 TODO: Deal with subnets
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050025"""
26
Scott Bakerf556b922014-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 Bhatiaf69dd5c2014-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
Scott Baker71b21f62014-12-17 18:22:33 -080045class XOSModelSerializer(serializers.ModelSerializer):
46 def save_object(self, obj, **kwargs):
47
48 """ rest_framework can't deal with ManyToMany relations that have a
49 through table. In plstackapi, most of the through tables we have
50 use defaults or blank fields, so there's no reason why we shouldn't
51 be able to save these objects.
52
53 So, let's strip out these m2m relations, and deal with them ourself.
54 """
55 obj._complex_m2m_data={};
56 if getattr(obj, '_m2m_data', None):
57 for relatedObject in obj._meta.get_all_related_many_to_many_objects():
58 if (relatedObject.field.rel.through._meta.auto_created):
59 # These are non-trough ManyToMany relations and
60 # can be updated just fine
61 continue
62 fieldName = relatedObject.get_accessor_name()
63 if fieldName in obj._m2m_data.keys():
64 obj._complex_m2m_data[fieldName] = (relatedObject, obj._m2m_data[fieldName])
65 del obj._m2m_data[fieldName]
66
67 serializers.ModelSerializer.save_object(self, obj, **kwargs);
68
69 for (accessor, stuff) in obj._complex_m2m_data.items():
70 (relatedObject, data) = stuff
71 through = relatedObject.field.rel.through
72 local_fieldName = relatedObject.field.m2m_reverse_field_name()
73 print "XXX", accessor, relatedObject, data
74 existing = through.objects.filter(**{local_fieldName: obj});
75 print "existing", existing
76
77
78
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050079{% for object in generator.all %}
80
81class {{ object.camel }}Serializer(serializers.HyperlinkedModelSerializer):
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -040082 id = serializers.Field()
83 {% for ref in object.refs %}
84 {% if ref.multi %}
85 {{ ref.plural }} = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='{{ ref }}-detail')
86 {% else %}
87 {{ ref }} = serializers.HyperlinkedRelatedField(read_only=True, view_name='{{ ref }}-detail')
88 {% endif %}
89 {% endfor %}
Scott Bakerb744f1c2014-11-21 11:32:24 -080090 humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
Scott Baker96eb3fd2014-11-25 00:53:25 -080091 validators = serializers.SerializerMethodField("getValidators")
Scott Bakerb744f1c2014-11-21 11:32:24 -080092 def getHumanReadableName(self, obj):
93 return str(obj)
Scott Baker96eb3fd2014-11-25 00:53:25 -080094 def getValidators(self, obj):
95 try:
96 return obj.getValidators()
97 except:
98 return None
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -040099 class Meta:
100 model = {{ object.camel }}
Scott Baker96eb3fd2014-11-25 00:53:25 -0800101 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 -0700102
Scott Baker71b21f62014-12-17 18:22:33 -0800103class {{ object.camel }}IdSerializer(XOSModelSerializer):
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -0400104 id = serializers.Field()
105 {% for ref in object.refs %}
106 {% if ref.multi %}
Scott Baker71b21f62014-12-17 18:22:33 -0800107 {{ ref.plural }} = serializers.PrimaryKeyRelatedField(many=True) #, read_only=True) #, view_name='{{ ref }}-detail')
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -0400108 {% else %}
Scott Baker71b21f62014-12-17 18:22:33 -0800109 {{ ref }} = serializers.PrimaryKeyRelatedField() # read_only=True) #, view_name='{{ ref }}-detail')
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -0400110 {% endif %}
111 {% endfor %}
Scott Bakerb744f1c2014-11-21 11:32:24 -0800112 humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
Scott Baker96eb3fd2014-11-25 00:53:25 -0800113 validators = serializers.SerializerMethodField("getValidators")
Scott Bakerb744f1c2014-11-21 11:32:24 -0800114 def getHumanReadableName(self, obj):
115 return str(obj)
Scott Baker96eb3fd2014-11-25 00:53:25 -0800116 def getValidators(self, obj):
117 try:
118 return obj.getValidators()
119 except:
120 return None
Scott Baker71b21f62014-12-17 18:22:33 -0800121 class Meta:
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -0400122 model = {{ object.camel }}
Scott Baker96eb3fd2014-11-25 00:53:25 -0800123 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 -0700124
125
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500126{% endfor %}
127
128serializerLookUp = {
129{% for object in generator.all %}
130 {{ object.camel }}: {{ object.camel }}Serializer,
131{% endfor %}
132 None: None,
133 }
134
Scott Baker89a7b7c2014-10-07 00:10:17 -0700135class PlanetStackRetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
136
137 # To handle fine-grained field permissions, we have to check can_update
138 # the object has been updated but before it has been saved.
139
140 def update(self, request, *args, **kwargs):
141 partial = kwargs.pop('partial', False)
142 self.object = self.get_object_or_none()
143
144 serializer = self.get_serializer(self.object, data=request.DATA,
145 files=request.FILES, partial=partial)
146
147 if not serializer.is_valid():
Scott Baker96eb3fd2014-11-25 00:53:25 -0800148 response = {"error": "validation",
149 "specific_error": "not serializer.is_valid()",
150 "reasons": serializer.errors}
151 return Response(response, status=status.HTTP_400_BAD_REQUEST)
Scott Baker89a7b7c2014-10-07 00:10:17 -0700152
153 try:
154 self.pre_save(serializer.object)
155 except ValidationError as err:
156 # full_clean on model instance may be called in pre_save,
157 # so we have to handle eventual errors.
Scott Baker96eb3fd2014-11-25 00:53:25 -0800158 response = {"error": "validation",
159 "specific_error": "ValidationError in pre_save",
160 "reasons": err.message_dict}
161 return Response(response, status=status.HTTP_400_BAD_REQUEST)
Scott Baker89a7b7c2014-10-07 00:10:17 -0700162
163 if serializer.object is not None:
164 if not serializer.object.can_update(request.user):
165 return Response(status=status.HTTP_400_BAD_REQUEST)
166
167 if self.object is None:
168 self.object = serializer.save(force_insert=True)
169 self.post_save(self.object, created=True)
170 return Response(serializer.data, status=status.HTTP_201_CREATED)
171
172 self.object = serializer.save(force_update=True)
173 self.post_save(self.object, created=False)
174 return Response(serializer.data, status=status.HTTP_200_OK)
175
176 def destroy(self, request, *args, **kwargs):
177 obj = self.get_object()
178 if obj.can_update(request.user):
Scott Bakeradcbf7a2014-10-09 11:09:15 -0700179 return super(generics.RetrieveUpdateDestroyAPIView, self).destroy(request, *args, **kwargs)
Scott Baker89a7b7c2014-10-07 00:10:17 -0700180 else:
181 return Response(status=status.HTTP_400_BAD_REQUEST)
182
183
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500184# Based on core/views/*.py
185{% for object in generator.all %}
186
187class {{ object.camel }}List(generics.ListCreateAPIView):
Sapan Bhatiadf2b49e2014-01-28 19:41:07 -0500188 queryset = {{ object.camel }}.objects.select_related().all()
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500189 serializer_class = {{ object.camel }}Serializer
Scott Baker9a270922014-07-03 18:01:30 -0700190 id_serializer_class = {{ object.camel }}IdSerializer
Scott Baker46b58542014-08-11 17:26:12 -0700191 filter_backends = (filters.DjangoFilterBackend,)
192 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 -0700193
194 def get_serializer_class(self):
195 no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
196 if (no_hyperlinks):
197 return self.id_serializer_class
198 else:
199 return self.serializer_class
200
Tony Mackeb8eb312014-02-04 20:50:39 -0500201 def get_queryset(self):
Tony Mack8f04ee32014-02-05 10:27:39 -0500202 return {{ object.camel }}.select_by_user(self.request.user)
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500203
Tony Mack9525eba2014-02-05 10:57:21 -0500204 def create(self, request, *args, **kwargs):
Scott Baker6ad17902014-11-12 01:12:28 -0800205 serializer = self.get_serializer(data=request.DATA, files=request.FILES)
206 if not (serializer.is_valid()):
Scott Baker96eb3fd2014-11-25 00:53:25 -0800207 response = {"error": "validation",
208 "specific_error": "not serializer.is_valid()",
209 "reasons": serializer.errors}
210 return Response(response, status=status.HTTP_400_BAD_REQUEST)
Scott Baker6ad17902014-11-12 01:12:28 -0800211 obj = serializer.object
Scott Bakeradcbf7a2014-10-09 11:09:15 -0700212 obj.caller = request.user
213 if obj.can_update(request.user):
214 return super({{ object.camel }}List, self).create(request, *args, **kwargs)
215 else:
216 raise Exception("failed obj.can_update")
217
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -0400218 ret = super({{ object.camel }}List, self).create(request, *args, **kwargs)
219 if (ret.status_code%100 != 200):
220 raise Exception(ret.data)
Scott Bakeradcbf7a2014-10-09 11:09:15 -0700221
222 return ret
223
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500224
Scott Baker89a7b7c2014-10-07 00:10:17 -0700225class {{ object.camel }}Detail(PlanetStackRetrieveUpdateDestroyAPIView):
Sapan Bhatiadf2b49e2014-01-28 19:41:07 -0500226 queryset = {{ object.camel }}.objects.select_related().all()
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500227 serializer_class = {{ object.camel }}Serializer
Scott Baker46b58542014-08-11 17:26:12 -0700228 id_serializer_class = {{ object.camel }}IdSerializer
229
230 def get_serializer_class(self):
231 no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
232 if (no_hyperlinks):
233 return self.id_serializer_class
234 else:
235 return self.serializer_class
Tony Mackeb8eb312014-02-04 20:50:39 -0500236
237 def get_queryset(self):
Tony Mack9525eba2014-02-05 10:57:21 -0500238 return {{ object.camel }}.select_by_user(self.request.user)
239
Scott Baker89a7b7c2014-10-07 00:10:17 -0700240 # update() is handled by PlanetStackRetrieveUpdateDestroyAPIView
Tony Mack9525eba2014-02-05 10:57:21 -0500241
Scott Baker89a7b7c2014-10-07 00:10:17 -0700242 # destroy() is handled by PlanetStackRetrieveUpdateDestroyAPIView
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500243
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500244{% endfor %}