blob: b3438c0a7aadf61c2131e91e20801549c006a1ad [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
Scott Bakercb60f8a2015-02-02 13:53:46 -080012from rest_framework.exceptions import PermissionDenied as RestFrameworkPermissionDenied
Scott Baker923f0962015-02-02 14:33:08 -080013from django.core.exceptions import PermissionDenied as DjangoPermissionDenied
Scott Baker8974e552015-02-10 19:26:00 -080014from xosapibase import XOSRetrieveUpdateDestroyAPIView, XOSListCreateAPIView
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050015
Scott Bakerb9973aa2014-12-23 11:13:52 -080016if hasattr(serializers, "ReadOnlyField"):
17 # rest_framework 3.x
18 IdField = serializers.ReadOnlyField
19else:
20 # rest_framework 2.x
21 IdField = serializers.Field
22
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050023"""
Sapan Bhatia970beb52014-10-08 11:34:23 -040024 Schema of the generator object:
25 all: Set of all Model objects
26 all_if(regex): Set of Model objects that match regex
Scott Bakerc480fb22014-11-12 01:12:28 -080027
Sapan Bhatia970beb52014-10-08 11:34:23 -040028 Model object:
29 plural: English plural of object name
30 camel: CamelCase version of object name
31 refs: list of references to other Model objects
32 props: list of properties minus refs
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050033
Sapan Bhatia970beb52014-10-08 11:34:23 -040034 TODO: Deal with subnets
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050035"""
36
Scott Baker8ffd7d72014-11-10 15:58:58 -080037def get_REST_patterns():
38 return patterns('',
Scott Bakerdd442112015-02-02 15:53:37 -080039 url(r'^xos/$', api_root),
Scott Baker8ffd7d72014-11-10 15:58:58 -080040 {% for object in generator.all %}
Scott Bakerdd442112015-02-02 15:53:37 -080041 url(r'xos/{{ object.rest_name }}/$', {{ object.camel }}List.as_view(), name='{{ object.singular }}-list'),
42 url(r'xos/{{ object.rest_name }}/(?P<pk>[a-zA-Z0-9\-]+)/$', {{ object.camel }}Detail.as_view(), name ='{{ object.singular }}-detail'),
Scott Baker8ffd7d72014-11-10 15:58:58 -080043 {% endfor %}
44 )
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050045
46@api_view(['GET'])
47def api_root(request, format=None):
48 return Response({
Sapan Bhatia970beb52014-10-08 11:34:23 -040049 {% for object in generator.all %}'{{ object.plural }}': reverse('{{ object }}-list', request=request, format=format),
50 {% endfor %}
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050051 })
52
53# Based on serializers.py
54
Scott Bakercac09742014-12-17 18:22:33 -080055class XOSModelSerializer(serializers.ModelSerializer):
56 def save_object(self, obj, **kwargs):
57
58 """ rest_framework can't deal with ManyToMany relations that have a
Scott Bakerdd442112015-02-02 15:53:37 -080059 through table. In xos, most of the through tables we have
Scott Bakercac09742014-12-17 18:22:33 -080060 use defaults or blank fields, so there's no reason why we shouldn't
61 be able to save these objects.
62
63 So, let's strip out these m2m relations, and deal with them ourself.
64 """
65 obj._complex_m2m_data={};
66 if getattr(obj, '_m2m_data', None):
67 for relatedObject in obj._meta.get_all_related_many_to_many_objects():
68 if (relatedObject.field.rel.through._meta.auto_created):
69 # These are non-trough ManyToMany relations and
70 # can be updated just fine
71 continue
72 fieldName = relatedObject.get_accessor_name()
73 if fieldName in obj._m2m_data.keys():
74 obj._complex_m2m_data[fieldName] = (relatedObject, obj._m2m_data[fieldName])
75 del obj._m2m_data[fieldName]
76
77 serializers.ModelSerializer.save_object(self, obj, **kwargs);
78
79 for (accessor, stuff) in obj._complex_m2m_data.items():
80 (relatedObject, data) = stuff
81 through = relatedObject.field.rel.through
82 local_fieldName = relatedObject.field.m2m_reverse_field_name()
Scott Baker148f5e12014-12-18 12:59:04 -080083 remote_fieldName = relatedObject.field.m2m_field_name()
84
85 # get the current set of existing relations
Scott Bakercac09742014-12-17 18:22:33 -080086 existing = through.objects.filter(**{local_fieldName: obj});
Scott Bakercac09742014-12-17 18:22:33 -080087
Scott Baker148f5e12014-12-18 12:59:04 -080088 data_ids = [item.id for item in data]
89 existing_ids = [getattr(item,remote_fieldName).id for item in existing]
Scott Bakercac09742014-12-17 18:22:33 -080090
Scott Baker148f5e12014-12-18 12:59:04 -080091 #print "data_ids", data_ids
92 #print "existing_ids", existing_ids
93
94 # remove relations that are in 'existing' but not in 'data'
95 for item in list(existing):
96 if (getattr(item,remote_fieldName).id not in data_ids):
97 print "delete", getattr(item,remote_fieldName)
98 item.delete() #(purge=True)
99
100 # add relations that are in 'data' but not in 'existing'
101 for item in data:
102 if (item.id not in existing_ids):
103 #print "add", item
104 newModel = through(**{local_fieldName: obj, remote_fieldName: item})
105 newModel.save()
Scott Bakercac09742014-12-17 18:22:33 -0800106
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500107{% for object in generator.all %}
108
109class {{ object.camel }}Serializer(serializers.HyperlinkedModelSerializer):
Scott Bakerb9973aa2014-12-23 11:13:52 -0800110 id = IdField()
Sapan Bhatia970beb52014-10-08 11:34:23 -0400111 {% for ref in object.refs %}
112 {% if ref.multi %}
113 {{ ref.plural }} = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='{{ ref }}-detail')
114 {% else %}
115 {{ ref }} = serializers.HyperlinkedRelatedField(read_only=True, view_name='{{ ref }}-detail')
116 {% endif %}
117 {% endfor %}
Scott Baker196ffef2014-11-21 11:32:24 -0800118 humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
Scott Baker351148c2014-11-25 00:53:25 -0800119 validators = serializers.SerializerMethodField("getValidators")
Scott Baker196ffef2014-11-21 11:32:24 -0800120 def getHumanReadableName(self, obj):
121 return str(obj)
Scott Baker351148c2014-11-25 00:53:25 -0800122 def getValidators(self, obj):
123 try:
124 return obj.getValidators()
125 except:
126 return None
Sapan Bhatia970beb52014-10-08 11:34:23 -0400127 class Meta:
128 model = {{ object.camel }}
Scott Baker351148c2014-11-25 00:53:25 -0800129 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 -0700130
Scott Bakercac09742014-12-17 18:22:33 -0800131class {{ object.camel }}IdSerializer(XOSModelSerializer):
Scott Bakerb9973aa2014-12-23 11:13:52 -0800132 id = IdField()
Sapan Bhatia970beb52014-10-08 11:34:23 -0400133 {% for ref in object.refs %}
134 {% if ref.multi %}
Scott Baker79348852014-12-23 10:43:03 -0800135 {{ ref.plural }} = serializers.PrimaryKeyRelatedField(many=True, queryset = {{ ref.camel }}.objects.all())
Sapan Bhatia970beb52014-10-08 11:34:23 -0400136 {% else %}
Scott Baker79348852014-12-23 10:43:03 -0800137 {{ ref }} = serializers.PrimaryKeyRelatedField( queryset = {{ ref.camel }}.objects.all())
Sapan Bhatia970beb52014-10-08 11:34:23 -0400138 {% endif %}
139 {% endfor %}
Scott Baker196ffef2014-11-21 11:32:24 -0800140 humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
Scott Baker351148c2014-11-25 00:53:25 -0800141 validators = serializers.SerializerMethodField("getValidators")
Scott Baker196ffef2014-11-21 11:32:24 -0800142 def getHumanReadableName(self, obj):
143 return str(obj)
Scott Baker351148c2014-11-25 00:53:25 -0800144 def getValidators(self, obj):
145 try:
146 return obj.getValidators()
147 except:
148 return None
Scott Bakercac09742014-12-17 18:22:33 -0800149 class Meta:
Sapan Bhatia970beb52014-10-08 11:34:23 -0400150 model = {{ object.camel }}
Scott Baker351148c2014-11-25 00:53:25 -0800151 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 -0700152
153
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500154{% endfor %}
155
Scott Baker8974e552015-02-10 19:26:00 -0800156serializerLookUp = {
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500157{% for object in generator.all %}
158 {{ object.camel }}: {{ object.camel }}Serializer,
159{% endfor %}
160 None: None,
161 }
162
163# Based on core/views/*.py
164{% for object in generator.all %}
165
Scott Baker8974e552015-02-10 19:26:00 -0800166class {{ object.camel }}List(XOSListCreateAPIView):
Sapan Bhatiadf2b49e2014-01-28 19:41:07 -0500167 queryset = {{ object.camel }}.objects.select_related().all()
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500168 serializer_class = {{ object.camel }}Serializer
Scott Baker9a270922014-07-03 18:01:30 -0700169 id_serializer_class = {{ object.camel }}IdSerializer
Scott Baker46b58542014-08-11 17:26:12 -0700170 filter_backends = (filters.DjangoFilterBackend,)
171 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 -0700172
173 def get_serializer_class(self):
Scott Baker71621062015-01-30 15:42:02 -0800174 no_hyperlinks=False
175 if hasattr(self.request,"QUERY_PARAMS"):
176 no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
Scott Baker9a270922014-07-03 18:01:30 -0700177 if (no_hyperlinks):
178 return self.id_serializer_class
179 else:
180 return self.serializer_class
181
Tony Mackeb8eb312014-02-04 20:50:39 -0500182 def get_queryset(self):
Scott Baker960431e2015-02-02 10:41:12 -0800183 if (not self.request.user.is_authenticated()):
Scott Bakercb60f8a2015-02-02 13:53:46 -0800184 raise RestFrameworkPermissionDenied("You must be authenticated in order to use this API")
Tony Mack8f04ee32014-02-05 10:27:39 -0500185 return {{ object.camel }}.select_by_user(self.request.user)
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500186
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500187
Scott Baker8974e552015-02-10 19:26:00 -0800188class {{ object.camel }}Detail(XOSRetrieveUpdateDestroyAPIView):
Sapan Bhatiadf2b49e2014-01-28 19:41:07 -0500189 queryset = {{ object.camel }}.objects.select_related().all()
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500190 serializer_class = {{ object.camel }}Serializer
Scott Baker46b58542014-08-11 17:26:12 -0700191 id_serializer_class = {{ object.camel }}IdSerializer
192
193 def get_serializer_class(self):
Scott Baker71621062015-01-30 15:42:02 -0800194 no_hyperlinks=False
195 if hasattr(self.request,"QUERY_PARAMS"):
196 no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
Scott Baker46b58542014-08-11 17:26:12 -0700197 if (no_hyperlinks):
198 return self.id_serializer_class
199 else:
200 return self.serializer_class
Scott Baker71621062015-01-30 15:42:02 -0800201
Tony Mackeb8eb312014-02-04 20:50:39 -0500202 def get_queryset(self):
Scott Baker960431e2015-02-02 10:41:12 -0800203 if (not self.request.user.is_authenticated()):
Scott Bakercb60f8a2015-02-02 13:53:46 -0800204 raise RestFrameworkPermissionDenied("You must be authenticated in order to use this API")
Tony Mack9525eba2014-02-05 10:57:21 -0500205 return {{ object.camel }}.select_by_user(self.request.user)
206
Scott Baker8974e552015-02-10 19:26:00 -0800207 # update() is handled by XOSRetrieveUpdateDestroyAPIView
Tony Mack9525eba2014-02-05 10:57:21 -0500208
Scott Baker8974e552015-02-10 19:26:00 -0800209 # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500210
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -0500211{% endfor %}