from rest_framework import serializers from rest_framework.validators import ValidationError from django.utils import timezone from .pendingModeration import PendingModeration from django.contrib.contenttypes.models import ContentType from .myModel import MyModel from .pendingModeration import PendingModerationSerializer from backend.utils import get_user_level from backend.permissions import is_moderation_required from backend.custom import MySerializerWithJSON 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 class MyModelSerializer(MySerializerWithJSON): 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) moderated_by = serializers.CharField(read_only=True) updated_by = serializers.CharField(read_only=True) pending_moderation = serializers.SerializerMethodField() model_config = serializers.SerializerMethodField() # 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() def get_model_config(self, obj=None): return self.Meta.model.model_config def get_pending_moderation(self, obj): pm = PendingModerationSerializer( obj.pending_moderation, many=True, read_only=True, context=self.context).data if self.context['view'].action == 'list': return len(pm) else: return pm def get_id(self, obj): return obj.pk class Meta: model = MyModel def my_validate(self, attrs): return attrs def validate(self, attrs): """ TODO unit test this """ self.user = self.context['request'].user self.user_level = get_user_level(self.user) if "obj_moderation_level" in attrs: requested_obj_moder_lv = attrs["obj_moderation_level"] 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) def set_model_attr_no_moder(self, moderated_and_updated): now = timezone.now() self.override_validated_data({ 'moderated_by': self.user, 'moderated_on': now, }) if moderated_and_updated: self.override_validated_data({ 'updated_by': self.user, 'updated_on': now, }) def clean_validated_data(self): self.override_validated_data(CLEANED_MY_MODEL_DATA) 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 def save(self, *args, **kwargs): return self.my_save(*args, **kwargs) def my_save(self, *args, **kwargs): self.clean_validated_data() self.my_pre_save() ct = ContentType.objects.get_for_model(self.Meta.model) if is_moderation_required(self.get_model_config()['moderation_level'], self.instance, self.user, self.user_level): if self.instance is None: # we need to create the main model self.instance = super( MyModelSerializer, self).save(*args, **kwargs) data_to_save = dict() for key in self.validated_data: try: # retrieve the submitted data and save the clean json # to make sure it will be savable data_to_save[key] = self.initial_data[key] except KeyError: pass data_to_save = override_data(data_to_save, CLEANED_MY_MODEL_DATA) 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 }) return self.instance else: moderated_and_updated = True if self.instance is None: self.set_model_attr_no_moder(moderated_and_updated) return super(MyModelSerializer, self).save(*args, **kwargs) else: try: pending_instance = PendingModeration.objects.get( content_type=ct, object_id=self.instance.pk, ) self.clean_validated_data() # Make that it is done... if pending_instance.new_object == self.validated_data: moderated_and_updated = False self.validated_data['updated_by'] = pending_instance.updated_by self.validated_data['updated_on'] = pending_instance.updated_on pending_instance.delete() except PendingModeration.DoesNotExist: pass self.set_model_attr_no_moder(moderated_and_updated) return super(MyModelSerializer, self).save(*args, **kwargs)