myModelSerializer.py 6.24 KB
Newer Older
1
from rest_framework import serializers
Florent Chehab's avatar
Florent Chehab committed
2
from rest_framework.validators import ValidationError
3
from django.utils import timezone
4
from .pendingModeration import PendingModeration
5
from django.contrib.contenttypes.models import ContentType
Florent Chehab's avatar
Florent Chehab committed
6
from .myModel import MyModel
Florent Chehab's avatar
Florent Chehab committed
7
from .pendingModeration import PendingModerationSerializer
Florent Chehab's avatar
Florent Chehab committed
8
from backend.utils import get_user_level
9
from backend.permissions import is_moderation_required
10
from backend.custom import MySerializerWithJSON
11

Florent Chehab's avatar
Florent Chehab committed
12
13
14
15
16
17
18
19
20
21
CLEANED_MY_MODEL_DATA = {
    'moderated_by': None,
    'moderated_on': None,
    'updated_by': None,
    'updated_on': None,
}


def override_data(old_data, new_data):
    for key in new_data:
Florent Chehab's avatar
Florent Chehab committed
22
23
        if key in old_data:
            old_data[key] = new_data[key]
Florent Chehab's avatar
Florent Chehab committed
24
25
26
    return old_data


Florent Chehab's avatar
Florent Chehab committed
27
class MyModelSerializer(MySerializerWithJSON):
28
29
30
31
    moderated_on = serializers.DateTimeField(
        format="%Y-%m-%d %H:%M:%S", read_only=True)
    updated_on = serializers.DateTimeField(
        format="%Y-%m-%d %H:%M:%S", read_only=True)
32
33
    moderated_by = serializers.CharField(read_only=True)
    updated_by = serializers.CharField(read_only=True)
34
    pending_moderation = serializers.SerializerMethodField()
Florent Chehab's avatar
Florent Chehab committed
35
    model_config = serializers.SerializerMethodField()
36

37
    # For easier handling on the client side, we force an id field
Florent Chehab's avatar
Florent Chehab committed
38
    # this is useful when a model has a dedicated primary key
39
40
    id = serializers.SerializerMethodField()

Florent Chehab's avatar
Florent Chehab committed
41
    def get_model_config(self, obj=None):
Florent Chehab's avatar
Florent Chehab committed
42
        return self.Meta.model.model_config
43

44
45
    FORCE_FULL_DISPLAY = False

46
    def get_pending_moderation(self, obj):
47
        if not self.FORCE_FULL_DISPLAY and self.context['view'].action == 'list':
48
            return None
49
        else:
50
51
52
53
            ct = ContentType.objects.get_for_model(self.Meta.model)
            pending = PendingModeration.objects.filter(
                content_type=ct, object_id=obj.pk)
            return PendingModerationSerializer(pending, many=True, read_only=True, context=self.context).data
54

55
56
57
    def get_id(self, obj):
        return obj.pk

58
59
60
    class Meta:
        model = MyModel

Florent Chehab's avatar
Florent Chehab committed
61
62
63
64
    def my_validate(self, attrs):
        return attrs

    def validate(self, attrs):
Florent Chehab's avatar
Florent Chehab committed
65
66
67
68
        """
        TODO unit test this
        """
        self.user = self.context['request'].user
Florent Chehab's avatar
Florent Chehab committed
69
70
71
72
        self.user_level = get_user_level(self.user)

        if "obj_moderation_level" in attrs:
            requested_obj_moder_lv = attrs["obj_moderation_level"]
73

Florent Chehab's avatar
Florent Chehab committed
74
75
76
77
78
79
            if requested_obj_moder_lv > self.user_level:
                raise ValidationError(
                    "You can't request moderation for a higher rank than you.")

        return self.my_validate(attrs)

80
81
    def set_model_attr_no_moder(self, moderated_and_updated):
        now = timezone.now()
Florent Chehab's avatar
Florent Chehab committed
82
        self.override_validated_data({
Florent Chehab's avatar
Florent Chehab committed
83
            'moderated_by': self.user,
Florent Chehab's avatar
Florent Chehab committed
84
85
            'moderated_on': now,
        })
86
87

        if moderated_and_updated:
Florent Chehab's avatar
Florent Chehab committed
88
            self.override_validated_data({
Florent Chehab's avatar
Florent Chehab committed
89
                'updated_by': self.user,
Florent Chehab's avatar
Florent Chehab committed
90
91
                'updated_on': now,
            })
92
93

    def clean_validated_data(self):
Florent Chehab's avatar
Florent Chehab committed
94
        self.override_validated_data(CLEANED_MY_MODEL_DATA)
95

Florent Chehab's avatar
Florent Chehab committed
96
97
98
99
100
101
102
103
104
105
    def override_validated_data(self, new_data):
        """
        Method used to force specific attributes when saving a model
        """
        for key in new_data:
            self.validated_data[key] = new_data[key]

    def my_pre_save(self):
        pass

Florent Chehab's avatar
Florent Chehab committed
106
107
    def save(self, *args, **kwargs):
        return self.my_save(*args, **kwargs)
108

Florent Chehab's avatar
Florent Chehab committed
109
    def my_save(self, *args, **kwargs):
110
        self.clean_validated_data()
Florent Chehab's avatar
Florent Chehab committed
111
        self.my_pre_save()
Florent Chehab's avatar
Florent Chehab committed
112
        ct = ContentType.objects.get_for_model(self.Meta.model)
113

Florent Chehab's avatar
Florent Chehab committed
114
        if is_moderation_required(self.get_model_config()['moderation_level'], self.instance, self.user, self.user_level):
115
            if self.instance is None:  # we need to create the main model
Florent Chehab's avatar
Florent Chehab committed
116
117
                # Store the user for squashing data in versions models
                self.validated_data.updated_by = self.user
118
                self.instance = super(
Florent Chehab's avatar
Florent Chehab committed
119
                    MyModelSerializer, self).save(*args, **kwargs)
120

Florent Chehab's avatar
Florent Chehab committed
121
122
123
            data_to_save = dict()
            for key in self.validated_data:
                try:
Florent Chehab's avatar
Florent Chehab committed
124
125
                    # retrieve the submitted data and save the clean json
                    # to make sure it will be savable
Florent Chehab's avatar
Florent Chehab committed
126
127
128
129
130
                    data_to_save[key] = self.initial_data[key]
                except KeyError:
                    pass

            data_to_save = override_data(data_to_save, CLEANED_MY_MODEL_DATA)
Florent Chehab's avatar
Florent Chehab committed
131
132
133
134
135
136
137
138
139

            PendingModeration.objects.update_or_create(
                content_type=ct,
                object_id=self.instance.pk,
                defaults={
                    'updated_on': timezone.now(),
                    'updated_by': self.user,
                    'new_object': data_to_save
                })
140
141
142
            return self.instance

        else:
143

144
            moderated_and_updated = True
145
            if self.instance is None:
146
                self.set_model_attr_no_moder(moderated_and_updated)
Florent Chehab's avatar
Florent Chehab committed
147
                return super(MyModelSerializer, self).save(*args, **kwargs)
148
            else:
Florent Chehab's avatar
Florent Chehab committed
149
150
151
152
153
                try:
                    pending_instance = PendingModeration.objects.get(
                        content_type=ct,
                        object_id=self.instance.pk,
                    )
154
                    self.clean_validated_data()  # Make that it is done...
Florent Chehab's avatar
Florent Chehab committed
155
156
157
158
159
160
161
162
163
                    # We have to compare the serialized data
                    # So we make sure to compare the same elements
                    key_to_remove = []
                    for key in self.initial_data:
                        if key not in self.validated_data:
                            key_to_remove.append(key)
                    for key in key_to_remove:
                        self.initial_data.pop(key, None)
                    if pending_instance.new_object == self.initial_data:
164
                        moderated_and_updated = False
Florent Chehab's avatar
Florent Chehab committed
165
166
167
                        self.validated_data['updated_by'] = pending_instance.updated_by
                        self.validated_data['updated_on'] = pending_instance.updated_on
                    pending_instance.delete()
168

Florent Chehab's avatar
Florent Chehab committed
169
170
                except PendingModeration.DoesNotExist:
                    pass
171

172
                self.set_model_attr_no_moder(moderated_and_updated)
Florent Chehab's avatar
Florent Chehab committed
173
                return super(MyModelSerializer, self).save(*args, **kwargs)