Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
DEFAULT_OBJ_MODERATION_LV, OBJ_MODERATION_PERMISSIONS, )
OBJ_MODERATION_PERMISSIONS[key] for key in OBJ_MODERATION_PERMISSIONS ]
# # # # # # # Module # # # # # # # #
""" All models in the app depend of this one. It contains the required attributes for managing optional data moderation.
All the logic behind moderation is done in EssentialModuleSerializer """
# store the update author User, null=True, on_delete=models.SET_NULL, related_name="+" ) # store the update date (model can be updated without moderation)
# store the moderator User, null=True, on_delete=models.SET_NULL, related_name="+" ) # store the moderation date
# Store the object moderation level by default default=DEFAULT_OBJ_MODERATION_LV, validators=[MinValueValidator(0), validate_obj_model_lv], ) # Add the link to pending moderation
# A bit of optimization: we store if there is something pending moderation
# # # # # # # Serializer # # # # # # # #
"moderated_by": None, "moderated_on": None, "updated_by": None, "updated_on": None, }
"""Update the data in old_data with the one in new_data """
old_data[key] = new_data[key]
"""Serializer to go along the EssentialModule Model. This serializer handles backend data moderation checks and tricks.
Raises: ValidationError -- If you are trying to moderate something you don't have rights to """
###### # Basic fields serializers
# Add a content_type_id field to be able to find versions
""" Serializer for content type """
""" Serializer for the `obj_info` *dynamic* field. Redefined. """ # In case some viewsets don't inherit from BaseModelViewSet and therefore # don't have the method to produce context["user_can_edit"] # Anyway, those Viewsets should be readonly, so we can return false.
try: fake_edit_request = FakeRequest(self.get_user_from_request(), "PUT") for permission_class in self.context["permission_classes"]: if not permission_class.has_object_permission( fake_edit_request, None, obj ): user_can_edit = False break except KeyError: pass
self.Meta.model, obj, self.get_user_from_request() )
"updated_by", "updated_on", "moderated_by", "moderated_on", "has_pending_moderation", "content_type_id", )
""" Validate `BaseModel` fields and enforce certain field at the backend level.
Checks that the requested moderation level is not higher than the one of the user. """ requested_obj_moder_lv = attrs["obj_moderation_level"]
try: user_level = get_user_level(self.get_user_from_request()) except KeyError: # if for some reason we don't have the user in the request # we set the level to the default one # this can occur during testing. user_level = DEFAULT_OBJ_MODERATION_LV
if requested_obj_moder_lv > user_level: raise ValidationError( "You can't request moderation for a higher rank than you." )
self, user, moderated_and_updated: bool ): """ Overrides model attributes regarding moderation and update. The moderated field is set to the request user. The moderated_on field is reset to now.
If there was an updated, the updated_by field and updated_on field are also reset. """
""" Clear fields related to update and moderation """
""" Method used to force specific attributes when saving a model """
""" Action to perform before saving a model """
""" Function that handles all the moderation in a smart way. Nothing has to be done to tell that we won't the data to be moderated, it is detected automatically. """
# Store the user for squashing data in versions models
# retrieve the submitted data and save the clean json # to make sure it will be savable
data_to_save, CLEANED_ESSENTIAL_MODULE_MODEL_DATA )
# Save instance into pending moderation state content_type=ct, object_id=self.instance.pk, defaults={ "updated_on": timezone.now(), "updated_by": user, "new_object": data_to_save, }, )
# Performance optimization, we store the fact that there is an object pending moderation
else: # Moderation is not needed, we need to check whether it's a moderation or an update with no moderation
user, moderated_and_updated ) else: content_type=ct, object_id=self.instance.pk ) # We have to compare the serialized data # So we make sure to compare the same elements key_to_remove.append(key) self.initial_data.pop(key, None)
user, moderated_and_updated )
# Performance optimization to know if has pending moderation
# # # # # # # ViewSet # # # # # # # #
""" Custom default viewset """
""" Override default function. Extra context is provided to the serializer class to know if a user can edit an element or not.
This allows to not do this query for all elements and improves performances. You can look at the comment below for more information. """
# When generating the API documentation (url: /api-doc) the request would be None # and we don't need to do anything special return super().get_serializer_context()
# Theoretically speaking we would need to use has_object_permission # But for performance purpose, we will consider edition right at the model # level. Which is consistent with our design. # Beware, that this might provide inconsistent data to the frontend # especially if permission_classes impact at the object level such as # IsOwner. # # Set check_obj_permissions_for_edit=True in your serializer # if you want a better check at the object level user_can_edit = False break
""" Extended default rest framework behavior to prefetch some table and enhance performances """ |