views.py 2.84 KB
Newer Older
1 2 3
"""
API view to allow manipulation of configuration models.
"""
4 5 6 7
from rest_framework.generics import CreateAPIView, RetrieveAPIView
from rest_framework.permissions import DjangoModelPermissions
from rest_framework.authentication import SessionAuthentication
from rest_framework.serializers import ModelSerializer
8
from django.db import transaction
9 10 11


class ReadableOnlyByAuthors(DjangoModelPermissions):
12
    """Only allow access by users with `add` permissions on the model."""
13 14 15 16
    perms_map = DjangoModelPermissions.perms_map.copy()
    perms_map['GET'] = perms_map['OPTIONS'] = perms_map['HEAD'] = perms_map['POST']


17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
class AtomicMixin(object):
    """Mixin to provide atomic transaction for as_view."""
    @classmethod
    def create_atomic_wrapper(cls, wrapped_func):
        """Returns a wrapped function."""
        def _create_atomic_wrapper(*args, **kwargs):
            """Actual wrapper."""
            # When a view call fails due to a permissions error, it raises an exception.
            # An uncaught exception breaks the DB transaction for any following DB operations
            # unless it's wrapped in a atomic() decorator or context manager.
            with transaction.atomic():
                return wrapped_func(*args, **kwargs)

        return _create_atomic_wrapper

    @classmethod
    def as_view(cls, **initkwargs):
        """Overrides as_view to add atomic transaction."""
        view = super(AtomicMixin, cls).as_view(**initkwargs)
        return cls.create_atomic_wrapper(view)


class ConfigurationModelCurrentAPIView(AtomicMixin, CreateAPIView, RetrieveAPIView):
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
    """
    This view allows an authenticated user with the appropriate model permissions
    to read and write the current configuration for the specified `model`.

    Like other APIViews, you can use this by using a url pattern similar to the following::

        url(r'config/example_config$', ConfigurationModelCurrentAPIView.as_view(model=ExampleConfig))
    """
    authentication_classes = (SessionAuthentication,)
    permission_classes = (ReadableOnlyByAuthors,)
    model = None

    def get_queryset(self):
        return self.model.objects.all()

    def get_object(self):
        # Return the currently active configuration
        return self.model.current()

    def get_serializer_class(self):
        if self.serializer_class is None:
            class AutoConfigModelSerializer(ModelSerializer):
62 63 64
                """Serializer class for configuration models."""
                class Meta(object):
                    """Meta information for AutoConfigModelSerializer."""
65 66 67 68 69 70 71 72
                    model = self.model

            self.serializer_class = AutoConfigModelSerializer

        return self.serializer_class

    def perform_create(self, serializer):
        # Set the requesting user as the one who is updating the configuration
73
        serializer.save(changed_by=self.request.user)