version.py 3.32 KB
Newer Older
1
2
3
4
5
6
7
from django.contrib.contenttypes.models import ContentType
from django.core import serializers as djangoSerializers
from django.core.serializers.base import DeserializationError
from rest_framework import serializers
from reversion.models import Version

from backend_app.models.abstract.base import BaseModelSerializer, BaseModelViewSet
8
from backend_app.permissions.app_permissions import ReadOnly
9
10
11
12
13
14
15
16
17


class VersionSerializer(BaseModelSerializer):
    """
    Custom serializer for the (reversion) version model
    """

    data = serializers.SerializerMethodField()

18
19
20
21
22
23
24
25
26
27
    serializers_mapping = None

    @classmethod
    def get_serializers_mapping(cls) -> dict:
        """
        Function that returns a mapping from model name to the serializer
        class that should be used to return the versioned data.
        """
        if cls.serializers_mapping is None:
            # Prevent cyclic imports
28
            from backend_app.viewsets import ALL_API_VIEWSETS
29
30
31
32

            # A little bit of optimization to easily find the serializer class associated with a model
            cls.serializers_mapping = dict()

33
            for viewset in ALL_API_VIEWSETS:
34
35
36
37
38
                serializer = viewset().get_serializer_class()
                model = serializer.Meta.model
                cls.serializers_mapping[model.__name__] = serializer

            # Override if models has a get_serializer method
39
            for viewset in ALL_API_VIEWSETS:
40
41
42
43
44
45
46
                model = viewset().get_serializer_class().Meta.model
                try:
                    cls.serializers_mapping[model.__name__] = model.get_serializer()
                except AttributeError:
                    pass
        return cls.serializers_mapping

47
48
    def get_data(self, obj):
        """
49
        Serializer for the data field
50
51
52
53
54
55
56
57
58
        """
        data = obj.serialized_data

        try:
            # We try to deserialize the version
            tmp = list(
                djangoSerializers.deserialize(obj.format, data, ignorenonexistent=True)
            )[0]
            # Version is valid,
59
            obj_serializer = self.get_serializers_mapping()[type(tmp.object).__name__]
60
61
62
63
64
            new_context = dict(self.context)
            new_context["view"].action = "list"
            return obj_serializer(tmp.object, context=new_context).data
        except (DeserializationError, djangoSerializers.SerializerDoesNotExist):
            # The version is not valid regarding the model, we should delete it !
Florent Chehab's avatar
Florent Chehab committed
65
            # This might be due to an updated model structure at some point.
66
            obj.delete()
Florent Chehab's avatar
Florent Chehab committed
67
68
            # We take care of the nb_versions field update with signals.
            # So it will remain coherent.
69
70
71
72
73
74
75
76
77
78
79
80
81
            return None

    class Meta:
        model = Version
        fields = ("data", "id")


class VersionViewSet(BaseModelViewSet):
    """
    Viewset for the versions
    """

    serializer_class = VersionSerializer
82
83
84
85
    permission_classes = (ReadOnly,)
    end_point_route = (
        r"versions/(?P<content_type_id>[0-9]+)/(?P<object_pk>[0-9A-Za-z]+)"
    )
86
87
88
89
90
91
92
93

    def get_queryset(self):
        content_type_id = self.kwargs["content_type_id"]
        object_pk = self.kwargs["object_pk"]
        ct = ContentType.objects.get_for_id(content_type_id)
        model = ct.model_class()
        obj = model.objects.get(pk=object_pk)
        return Version.objects.get_for_object(obj)