taiga-back/greenmine/base/api.py

199 lines
6.3 KiB
Python

# -*- coding: utf-8 -*-
from django.db import transaction
from rest_framework import viewsets
from rest_framework import status
from rest_framework import mixins
from rest_framework import decorators as rf_decorators
from rest_framework.response import Response
from reversion.revisions import revision_context_manager
from reversion.models import Version
import reversion
from . import pagination
from . import serializers
from . import decorators
class CreateModelMixin(mixins.CreateModelMixin):
@transaction.atomic
def create(self, *args, **kwargs):
return super().create(*args, **kwargs)
class RetrieveModelMixin(mixins.RetrieveModelMixin):
@transaction.atomic
def retrieve(self, *args, **kwargs):
return super().retrieve(*args, **kwargs)
class UpdateModelMixin(mixins.UpdateModelMixin):
@transaction.atomic
def update(self, *args, **kwargs):
return super().update(*args, **kwargs)
class ListModelMixin(mixins.ListModelMixin):
@transaction.atomic
def list(self, *args, **kwargs):
return super().list(*args, **kwargs)
class DestroyModelMixin(mixins.DestroyModelMixin):
"""
Self version of DestroyModelMixin with
pre_delete hook method.
"""
def pre_delete(self, obj):
pass
@transaction.atomic
def destroy(self, request, *args, **kwargs):
obj = self.get_object()
self.pre_delete(obj)
obj.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class PreconditionMixin(object):
def pre_conditions_on_save(self, obj):
pass
def pre_conditions_on_delete(self, obj):
pass
def pre_save(self, obj):
super().pre_save(obj)
self.pre_conditions_on_save(obj)
def pre_delete(self, obj):
super().pre_delete(obj)
self.pre_conditions_on_delete(obj)
class DetailAndListSerializersMixin(object):
"""
Use a diferent serializer class to the list action.
"""
list_serializer_class = None
def get_serializer_class(self):
if self.action == "list" and self.list_serializer_class:
return self.list_serializer_class
return super().get_serializer_class()
class ReversionMixin(object):
historical_model = Version
historical_serializer_class = serializers.VersionSerializer
historical_paginate_by = 5
def get_historical_queryset(self):
return reversion.get_unique_for_object(self.get_object())
def get_historical_serializer_class(self):
serializer_class = self.historical_serializer_class
if serializer_class is not None:
return serializer_class
assert self.historical_model is not None, \
"'%s' should either include a 'serializer_class' attribute, " \
"or use the 'model' attribute as a shortcut for " \
"automatically generating a serializer class." \
% self.__class__.__name__
class DefaultSerializer(self.model_serializer_class):
class Meta:
model = self.historical_model
return DefaultSerializer
def get_historical_serializer(self, instance=None, data=None, files=None,
many=False, partial=False):
serializer_class = self.get_historical_serializer_class()
return serializer_class(instance, data=data, files=files,
many=many, partial=partial)
def get_historical_pagination_serializer(self, page):
return self.get_historical_serializer(page.object_list, many=True)
@rf_decorators.link()
@decorators.change_instance_attr("paginate_by", historical_paginate_by)
def historical(self, request, *args, **kwargs):
obj = self.get_object()
self.historical_object_list = self.get_historical_queryset()
# Switch between paginated or standard style responses
page = self.paginate_queryset(self.historical_object_list)
if page is not None:
serializer = self.get_historical_pagination_serializer(page)
else:
serializer = self.get_historical_serializer(self.historical_object_list,
many=True)
return Response(serializer.data)
@rf_decorators.action()
def restore(self, request, vpk=None, *args, **kwargs):
if not vpk:
return Response(status=status.HTTP_404_NOT_FOUND)
try:
version = reversion.get_for_object(self.get_object()).get(pk=vpk)
version.revision.revert(delete=True)
serializer = self.get_serializer(self.get_object())
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_202_ACCEPTED,
headers=headers)
except Version.DoesNotExist:
return Response(status=status.HTTP_400_BAD_REQUEST)
def dispatch(self, request, *args, **kwargs):
revision_context_manager.start()
try:
response = super().dispatch(request, *args, **kwargs)
except Exception as e:
revision_context_manager.invalidate()
revision_context_manager.end()
raise
if self.request.user.is_authenticated():
revision_context_manager.set_user(self.request.user)
if response.status_code > 206:
revision_context_manager.invalidate()
revision_context_manager.end()
return response
class ModelCrudViewSet(DetailAndListSerializersMixin,
ReversionMixin,
PreconditionMixin,
pagination.HeadersPaginationMixin,
pagination.ConditionalPaginationMixin,
CreateModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
DestroyModelMixin,
ListModelMixin,
viewsets.GenericViewSet):
pass
class ModelListViewSet(DetailAndListSerializersMixin,
ReversionMixin,
PreconditionMixin,
pagination.HeadersPaginationMixin,
pagination.ConditionalPaginationMixin,
RetrieveModelMixin,
ListModelMixin,
viewsets.GenericViewSet):
pass