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 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
Scott Baker4af864e2015-02-02 13:53:46 -080012from rest_framework.exceptions import PermissionDenied as RestFrameworkPermissionDenied
Scott Baker15af5652015-02-02 14:33:08 -080013from django.core.exceptions import PermissionDenied as DjangoPermissionDenied
Scott Baker96e2b2c2015-02-10 19:26:00 -080014from xosapibase import XOSRetrieveUpdateDestroyAPIView, XOSListCreateAPIView
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050015
Scott Bakera009d562014-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 Bhatiaf69dd5c2014-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 Baker6ad17902014-11-12 01:12:28 -080027
Sapan Bhatiaf69dd5c2014-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 Bhatiaf69dd5c2014-10-08 11:34:23 -040034 TODO: Deal with subnets
Sapan Bhatia3bbf5ed2014-01-13 13:29:12 -050035"""
36
Scott Bakerf556b922014-11-10 15:58:58 -080037def get_REST_patterns():
38 return patterns('',
Scott Baker1faefd32015-02-02 15:53:37 -080039 url(r'^xos/$', api_root),
Scott Bakerf556b922014-11-10 15:58:58 -080040 {% for object in generator.all %}
Scott Baker1faefd32015-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 Bakerf556b922014-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 Bhatiaf69dd5c2014-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 Baker71b21f62014-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 Baker1faefd32015-02-02 15:53:37 -080059 through table. In xos, most of the through tables we have
Scott Baker71b21f62014-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 Bakerd923b812014-12-18 12:59:04 -080083 remote_fieldName = relatedObject.field.m2m_field_name()
84
85 # get the current set of existing relations
Scott Baker71b21f62014-12-17 18:22:33 -080086 existing = through.objects.filter(**{local_fieldName: obj});
Scott Baker71b21f62014-12-17 18:22:33 -080087
Scott Bakerd923b812014-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 Baker71b21f62014-12-17 18:22:33 -080090
Scott Bakerd923b812014-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 Baker71b21f62014-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 Bakera009d562014-12-23 11:13:52 -0800110 id = IdField()
Sapan Bhatiaf69dd5c2014-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 Bakerb744f1c2014-11-21 11:32:24 -0800118 humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
Scott Baker96eb3fd2014-11-25 00:53:25 -0800119 validators = serializers.SerializerMethodField("getValidators")
Scott Bakerb744f1c2014-11-21 11:32:24 -0800120 def getHumanReadableName(self, obj):
121 return str(obj)
Scott Baker96eb3fd2014-11-25 00:53:25 -0800122 def getValidators(self, obj):
123 try:
124 return obj.getValidators()
125 except:
126 return None
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -0400127 class Meta:
128 model = {{ object.camel }}
Scott Baker96eb3fd2014-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 Baker71b21f62014-12-17 18:22:33 -0800131class {{ object.camel }}IdSerializer(XOSModelSerializer):
Scott Bakera009d562014-12-23 11:13:52 -0800132 id = IdField()
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -0400133 {% for ref in object.refs %}
134 {% if ref.multi %}
Scott Baker0461aee2014-12-23 10:43:03 -0800135 {{ ref.plural }} = serializers.PrimaryKeyRelatedField(many=True, queryset = {{ ref.camel }}.objects.all())
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -0400136 {% else %}
Scott Baker0461aee2014-12-23 10:43:03 -0800137 {{ ref }} = serializers.PrimaryKeyRelatedField( queryset = {{ ref.camel }}.objects.all())
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -0400138 {% endif %}
139 {% endfor %}
Scott Bakerb744f1c2014-11-21 11:32:24 -0800140 humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
Scott Baker96eb3fd2014-11-25 00:53:25 -0800141 validators = serializers.SerializerMethodField("getValidators")
Scott Bakerb744f1c2014-11-21 11:32:24 -0800142 def getHumanReadableName(self, obj):
143 return str(obj)
Scott Baker96eb3fd2014-11-25 00:53:25 -0800144 def getValidators(self, obj):
145 try:
146 return obj.getValidators()
147 except:
148 return None
Scott Baker71b21f62014-12-17 18:22:33 -0800149 class Meta:
Sapan Bhatiaf69dd5c2014-10-08 11:34:23 -0400150 model = {{ object.camel }}
Scott Baker96eb3fd2014-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 Baker96e2b2c2015-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 Baker96e2b2c2015-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 Bakercc184792015-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 Baker20101af2015-02-02 10:41:12 -0800183 if (not self.request.user.is_authenticated()):
Scott Baker4af864e2015-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 Baker96e2b2c2015-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 Bakercc184792015-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 Bakercc184792015-01-30 15:42:02 -0800201
Tony Mackeb8eb312014-02-04 20:50:39 -0500202 def get_queryset(self):
Scott Baker20101af2015-02-02 10:41:12 -0800203 if (not self.request.user.is_authenticated()):
Scott Baker4af864e2015-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 Baker96e2b2c2015-02-10 19:26:00 -0800207 # update() is handled by XOSRetrieveUpdateDestroyAPIView
Tony Mack9525eba2014-02-05 10:57:21 -0500208
Scott Baker96e2b2c2015-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 %}