myModelSerializer.py 5.43 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
Florent Chehab's avatar
Florent Chehab committed
10
from backend.models.tools import MySerializerWithJSON
11

Florent Chehab's avatar
Florent Chehab committed
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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:
        old_data[key] = new_data[key]
    return old_data


Florent Chehab's avatar
Florent Chehab committed
26
class MyModelSerializer(MySerializerWithJSON):
27
28
29
30
    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)
31
32
    moderated_by = serializers.CharField(read_only=True)
    updated_by = serializers.CharField(read_only=True)
33
    pending_moderation = serializers.SerializerMethodField()
Florent Chehab's avatar
Florent Chehab committed
34
    model_config = serializers.SerializerMethodField()
35

36
37
38
39
    # For easier handling on the client side, we force an id field
    # this is usefull when a model has a dedicated primary key
    id = serializers.SerializerMethodField()

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

43
44
45
46
47
    def get_pending_moderation(self, obj):
        if self.context['view'].action != 'list':
            return PendingModerationSerializer(obj.pending_moderation, many=True, read_only=True, context=self.context).data
        return None

48
49
50
    def get_id(self, obj):
        return obj.pk

51
52
53
    class Meta:
        model = MyModel

Florent Chehab's avatar
Florent Chehab committed
54
55
56
57
    def my_validate(self, attrs):
        return attrs

    def validate(self, attrs):
Florent Chehab's avatar
Florent Chehab committed
58
59
60
61
        """
        TODO unit test this
        """
        self.user = self.context['request'].user
Florent Chehab's avatar
Florent Chehab committed
62
63
64
65
        self.user_level = get_user_level(self.user)

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

Florent Chehab's avatar
Florent Chehab committed
67
68
69
70
71
72
            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)

73
74
    def set_model_attr_no_moder(self, moderated_and_updated):
        now = timezone.now()
Florent Chehab's avatar
Florent Chehab committed
75
        self.override_validated_data({
Florent Chehab's avatar
Florent Chehab committed
76
            'moderated_by': self.user,
Florent Chehab's avatar
Florent Chehab committed
77
78
            'moderated_on': now,
        })
79
80

        if moderated_and_updated:
Florent Chehab's avatar
Florent Chehab committed
81
            self.override_validated_data({
Florent Chehab's avatar
Florent Chehab committed
82
                'updated_by': self.user,
Florent Chehab's avatar
Florent Chehab committed
83
84
                'updated_on': now,
            })
85
86

    def clean_validated_data(self):
Florent Chehab's avatar
Florent Chehab committed
87
        self.override_validated_data(CLEANED_MY_MODEL_DATA)
88

Florent Chehab's avatar
Florent Chehab committed
89
90
91
92
93
94
95
96
97
98
    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
99
100
    def save(self, *args, **kwargs):
        return self.my_save(*args, **kwargs)
101

Florent Chehab's avatar
Florent Chehab committed
102
    def my_save(self, *args, **kwargs):
103
        self.clean_validated_data()
Florent Chehab's avatar
Florent Chehab committed
104
        self.my_pre_save()
Florent Chehab's avatar
Florent Chehab committed
105
        ct = ContentType.objects.get_for_model(self.Meta.model)
106

Florent Chehab's avatar
Florent Chehab committed
107
        if is_moderation_required(self.get_model_config()['moderation_level'], self.instance, self.user, self.user_level):
108
109
            if self.instance is None:  # we need to create the main model
                self.instance = super(
Florent Chehab's avatar
Florent Chehab committed
110
                    MyModelSerializer, self).save(*args, **kwargs)
111

Florent Chehab's avatar
Florent Chehab committed
112
113
114
            data_to_save = dict()
            for key in self.validated_data:
                try:
Florent Chehab's avatar
Florent Chehab committed
115
116
                    # retrieve the submitted data and save the clean json
                    # to make sure it will be savable
Florent Chehab's avatar
Florent Chehab committed
117
118
119
120
121
                    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
122
123
124
125
126
127
128
129
130

            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
                })
131
132
133
            return self.instance

        else:
134

135
            moderated_and_updated = True
136
            if self.instance is None:
137
                self.set_model_attr_no_moder(moderated_and_updated)
Florent Chehab's avatar
Florent Chehab committed
138
                return super(MyModelSerializer, self).save(*args, **kwargs)
139
            else:
Florent Chehab's avatar
Florent Chehab committed
140
141
142
143
144
                try:
                    pending_instance = PendingModeration.objects.get(
                        content_type=ct,
                        object_id=self.instance.pk,
                    )
145
                    self.clean_validated_data()  # Make that it is done...
Florent Chehab's avatar
Florent Chehab committed
146
                    if pending_instance.new_object == self.validated_data:
147
                        moderated_and_updated = False
Florent Chehab's avatar
Florent Chehab committed
148
149
150
                        self.validated_data['updated_by'] = pending_instance.updated_by
                        self.validated_data['updated_on'] = pending_instance.updated_on
                    pending_instance.delete()
151

Florent Chehab's avatar
Florent Chehab committed
152
153
                except PendingModeration.DoesNotExist:
                    pass
154

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