def model_viewset_factory(build_model, build_filter_class, build_serializer_class, build_detail_serializer_class, build_queryset, build_actions, ordering_field): ReorderSerializer = reorder_serializer_factory(build_queryset, ordering_field) class Viewset(viewsets.ModelViewSet): model = build_model queryset = build_queryset pagination_class = CustomPageNumberPagination filter_class = build_filter_class authentication_classes = () permission_classes = (HasProjectPermissions, ModifyNotInDemo) def get_serializer_class(self): if self.action == 'aggregate': return AggregateSerializer elif self.action == 'group': return GroupSerializer elif self.action == 'retrieve': return build_detail_serializer_class else: return build_serializer_class @list_route(methods=['get']) def aggregate(self, request): queryset = self.filter_queryset(self.get_queryset()) y_func = request.GET['_y_func'].lower() y_column = request.GET.get('_y_column', 'id') y_field = self.model._meta.get_field(y_column) y_serializer_class, y_serializer_kwargs = ModelSerializer().build_standard_field(y_column, y_field) y_serializer = y_serializer_class(**y_serializer_kwargs) queryset = AggregateFilter().filter(queryset, { 'y_func': y_func, 'y_column': y_column }) serializer = self.get_serializer( queryset, y_func_serializer=y_serializer ) return Response(serializer.data) @list_route(methods=['get']) def group(self, request): queryset = self.filter_queryset(self.get_queryset()) x_column = request.GET['_x_column'] x_lookup_name = request.GET.get('_x_lookup') y_func = request.GET['_y_func'].lower() y_column = request.GET.get('_y_column', 'id') x_field = self.model._meta.get_field(x_column) x_lookup = x_field.class_lookups.get(x_lookup_name) y_field = self.model._meta.get_field(y_column) if x_lookup: x_field = x_lookup('none').output_field x_serializer_class, x_serializer_kwargs = ModelSerializer().build_standard_field(x_column, x_field) x_serializer = x_serializer_class(**x_serializer_kwargs) y_serializer_class, y_serializer_kwargs = ModelSerializer().build_standard_field(y_column, y_field) y_serializer = y_serializer_class(**y_serializer_kwargs) queryset = GroupFilter().filter(queryset, { 'x_column': x_column, 'x_lookup': x_lookup, 'y_func': y_func, 'y_column': y_column }) serializer = self.get_serializer( queryset, many=True, group_serializer=x_serializer, y_func_serializer=y_serializer ) return Response(serializer.data) def get_serializer(self, *args, **kwargs): """ Return the serializer instance that should be used for validating and deserializing input, and for serializing output. """ serializer_class = self.get_serializer_class() kwargs['context'] = self.get_serializer_context() return serializer_class(*args, **kwargs) @list_route(methods=['post']) def reorder(self, request): serializer = ReorderSerializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) @list_route(methods=['post']) def reset_order(self, request): i = 1 for instance in build_queryset: setattr(instance, ordering_field, i) instance.save() i += 1 return Response({}) for action in build_actions: def route(self, request): form = action(data=request.data) if not form.is_valid(): return Response(form.errors, status=status.HTTP_400_BAD_REQUEST) queryset = form.filer_queryset(self.get_queryset()) try: result = form.save(queryset) except Exception as e: return Response({NON_FIELD_ERRORS: str(e)}, status=status.HTTP_400_BAD_REQUEST) return Response({'action': form._meta.name, 'result': result}) decorator = list_route(methods=['post']) route = decorator(route) setattr(Viewset, action._meta.name, route) return Viewset
def model_viewset_factory(build_model, build_filter_class, build_serializer_class, build_detail_serializer_class, build_queryset): ReorderSerializer = reorder_serializer_factory(build_queryset) ResetOrderSerializer = reset_order_serializer_factory(build_queryset) class Viewset(MethodOverrideViewMixin, CORSAPIViewMixin, viewsets.ModelViewSet): model = build_model queryset = build_queryset pagination_class = CustomPageNumberPagination filter_class = build_filter_class authentication_classes = () permission_classes = (HasProjectPermissions, ModifyNotInDemo) def filter_queryset(self, queryset): queryset = super().filter_queryset(queryset) if self.action == 'list': pk = self.model._meta.pk.name ordering = queryset.query.order_by if len(ordering) == 0 and len(self.model._meta.ordering): ordering = self.model._meta.ordering if not any(map(lambda x: x == pk or x == '-{}'.format(pk) or x == '-pk', ordering)): order_by = list(ordering) + ['-pk'] queryset = queryset.order_by(*order_by) return queryset @property def required_project_permission(self): return { 'permission_type': 'model', 'permission_object': self.kwargs['model'], 'permission_actions': { 'create': 'w', 'update': 'w', 'partial_update': 'w', 'destroy': 'd', 'retrieve': 'r', 'list': 'r', 'aggregate': 'r', 'group': 'r', 'reorder': 'w', 'reset_order': 'w', 'get_siblings': 'r' }.get(self.action, 'w') } def get_serializer_class(self): if self.action == 'aggregate': return AggregateSerializer elif self.action == 'group': return GroupSerializer elif self.action == 'retrieve': return build_detail_serializer_class else: return build_serializer_class @list_route(methods=['get']) def aggregate(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) y_func = request.GET['_y_func'].lower() y_column = request.GET.get('_y_column', 'id') y_field = self.model._meta.get_field(y_column) y_serializer_class, y_serializer_kwargs = ModelSerializer().build_standard_field(y_column, y_field) y_serializer = y_serializer_class(**y_serializer_kwargs) queryset = AggregateFilter().filter(queryset, { 'y_func': y_func, 'y_column': y_column }) serializer = self.get_serializer( queryset, y_func_serializer=y_serializer ) return Response(serializer.data) @list_route(methods=['get']) def group(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) x_column = request.GET['_x_column'] x_lookup_name = request.GET.get('_x_lookup') y_func = request.GET['_y_func'].lower() y_column = request.GET.get('_y_column', 'id') x_field = self.model._meta.get_field(x_column) x_lookup = x_field.class_lookups.get(x_lookup_name) y_field = self.model._meta.get_field(y_column) if x_lookup: x_field = x_lookup('none').output_field x_serializer_class, x_serializer_kwargs = ModelSerializer().build_standard_field(x_column, x_field) x_serializer = x_serializer_class(**x_serializer_kwargs) y_serializer_class, y_serializer_kwargs = ModelSerializer().build_standard_field(y_column, y_field) y_serializer = y_serializer_class(**y_serializer_kwargs) queryset = GroupFilter().filter(queryset, { 'x_column': x_column, 'x_lookup': x_lookup, 'y_func': y_func, 'y_column': y_column }) serializer = self.get_serializer( queryset, many=True, group_serializer=x_serializer, y_func_serializer=y_serializer ) return Response(serializer.data) def get_serializer(self, *args, **kwargs): """ Return the serializer instance that should be used for validating and deserializing input, and for serializing output. """ serializer_class = self.get_serializer_class() kwargs['context'] = self.get_serializer_context() return serializer_class(*args, **kwargs) @list_route(methods=['post']) def reorder(self, request, *args, **kwargs): serializer = ReorderSerializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) @list_route(methods=['post']) def reset_order(self, request, *args, **kwargs): serializer = ResetOrderSerializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) @detail_route(methods=['get']) def get_siblings(self, request, *args, **kwargs): lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} obj = get_object_or_404(self.get_queryset(), **filter_kwargs) # May raise a permission denied self.check_object_permissions(self.request, obj) queryset = self.filter_queryset(self.get_queryset()) return Response(get_model_siblings(self.model, obj, queryset)) return Viewset