myModelVersionned.py 3.77 KB
Newer Older
Florent Chehab's avatar
Florent Chehab committed
1
2
3
from django.contrib.contenttypes.models import ContentType
from django.core import serializers as djangoSerializers
from django.core.serializers.base import DeserializationError
4
from django.db import models
Florent Chehab's avatar
Florent Chehab committed
5
6
7

import reversion
from backend_app.custom import MySerializerWithJSON
8
9
10
11
12
from backend_app.models.abstract.my_model import (
    MyModel,
    MyModelSerializer,
    MyModelViewSet,
)
Florent Chehab's avatar
Florent Chehab committed
13
14
from backend_app.signals.__squash_revision_by_user import new_revision_saved
from backend_app.utils import get_viewset_permissions
Florent Chehab's avatar
Florent Chehab committed
15
16
from rest_framework import mixins, serializers, viewsets
from reversion.models import Version
Florent Chehab's avatar
Florent Chehab committed
17
18
19


class MyModelVersionned(MyModel):
Florent Chehab's avatar
Florent Chehab committed
20
21
22
23
    """
    Custom MyModel that will be versionned in the app
    """

24
25
26
    # We store the current number of versions for better performance
    nb_versions = models.PositiveIntegerField(default=0)

27
28
    @classmethod
    def get_serializer(cls):
29
30
        """
            This function is required for handling
31
            versioning easily.
32
33
            You have to put the correct value in each subclass
        """
34
        raise Exception("Get_serializer must be redifined in subclass")
35

Florent Chehab's avatar
Florent Chehab committed
36
37
38
39
40
    class Meta:
        abstract = True


class MyModelVersionnedSerializer(MyModelSerializer):
Florent Chehab's avatar
Florent Chehab committed
41
42
43
44
45
    """
    Serializer for versionned models
    """

    # Add a nb_versions field
46
47
    nb_versions = serializers.IntegerField(read_only=True)

Florent Chehab's avatar
Florent Chehab committed
48
    # Add a content_type_id field to be able to find versions
49
50
51
    content_type_id = serializers.SerializerMethodField()

    def get_content_type_id(self, obj):
Florent Chehab's avatar
Florent Chehab committed
52
53
54
        """
        Serializer for content type
        """
55
        return ContentType.objects.get_for_model(self.Meta.model).id
Florent Chehab's avatar
Florent Chehab committed
56

Florent Chehab's avatar
Florent Chehab committed
57
    def save(self, *args, **kwargs):
Florent Chehab's avatar
Florent Chehab committed
58
59
60
61
        """
        Custom save function to use reversion.
        """

Florent Chehab's avatar
Florent Chehab committed
62
        with reversion.create_revision():
63
            res = super().save(*args, **kwargs)
Florent Chehab's avatar
Florent Chehab committed
64
            reversion.set_user(res.updated_by)
Florent Chehab's avatar
Florent Chehab committed
65
        new_revision_saved.send(sender=self.__class__, obj=self.instance)
66
        return res
Florent Chehab's avatar
Florent Chehab committed
67
68
69


class MyModelVersionnedViewSet(MyModelViewSet):
Florent Chehab's avatar
Florent Chehab committed
70
71
72
73
    """
    Viewset for the versionned models
    """

Florent Chehab's avatar
Florent Chehab committed
74
    serializer_class = MyModelVersionnedSerializer
75
76


Florent Chehab's avatar
Florent Chehab committed
77
class VersionSerializer(MySerializerWithJSON):
Florent Chehab's avatar
Florent Chehab committed
78
79
80
81
    """
    Custom serializer for the (reversion) version model
    """

82
83
84
    data = serializers.SerializerMethodField()

    def get_data(self, obj):
Florent Chehab's avatar
Florent Chehab committed
85
        """
Florent Chehab's avatar
Florent Chehab committed
86
        Serilizer for the data field
Florent Chehab's avatar
Florent Chehab committed
87
        """
88
89
90
        data = obj.serialized_data

        try:
Florent Chehab's avatar
Florent Chehab committed
91
            # We try to deserialize the version
92
93
94
            tmp = list(
                djangoSerializers.deserialize(obj.format, data, ignorenonexistent=True)
            )[0]
95
96
            # Version is valid,
            obj_serializer = tmp.object.get_serializer()
Florent Chehab's avatar
Florent Chehab committed
97
            new_context = dict(self.context)
98
            new_context["view"].action = "list"
Florent Chehab's avatar
Florent Chehab committed
99
            return obj_serializer(tmp.object, context=new_context).data
100
        except (DeserializationError, djangoSerializers.SerializerDoesNotExist):
Florent Chehab's avatar
Florent Chehab committed
101
102
103
            # The version is not valid regarding the model, we should delete it !
            # This might be due to an updated model.
            obj.delete()
104
105
106
107
108
            # Might result in inconsistent nb of versions but that's fine.
            return None

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


Florent Chehab's avatar
Florent Chehab committed
112
113
114
115
116
class VersionViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    """
    Viewset for the versions
    """

117
    permission_classes = get_viewset_permissions("VersionViewSet")
118
119
120
    serializer_class = VersionSerializer

    def get_queryset(self):
121
122
        content_type_id = self.kwargs["content_type_id"]
        object_pk = self.kwargs["object_pk"]
123
124
125
126
        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)