Skip to content
Snippets Groups Projects
serializers.py 3.01 KiB
Newer Older
  • Learn to ignore specific revisions
  • from rest_framework import serializers
    
    
    class ActionSerializer(serializers.Serializer):
        """
        A special serializer that can operate on a list of objects
        and apply actions on it.
        """
    
        action = serializers.CharField(required=True)
        objects = serializers.JSONField(required=True)
        filters = serializers.DictField(required=False)
        actions = None
        filterset_class = None
    
        # those are actions identifier where we don't want to allow the "all"
        # selector because it's to dangerous. Like object deletion.
        dangerous_actions = []
    
    
        def __init__(self, *args, **kwargs):
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            self.queryset = kwargs.pop("queryset")
    
            if self.actions is None:
                raise ValueError(
    
    Eliot Berriot's avatar
    Eliot Berriot committed
                    "You must declare a list of actions on " "the serializer class"
                )
    
    
            for action in self.actions:
    
    Eliot Berriot's avatar
    Eliot Berriot committed
                handler_name = "handle_{}".format(action)
                assert hasattr(self, handler_name), "{} miss a {} method".format(
                    self.__class__.__name__, handler_name
    
                )
            super().__init__(self, *args, **kwargs)
    
        def validate_action(self, value):
            if value not in self.actions:
                raise serializers.ValidationError(
    
    Eliot Berriot's avatar
    Eliot Berriot committed
                    "{} is not a valid action. Pick one of {}.".format(
                        value, ", ".join(self.actions)
    
                    )
                )
            return value
    
        def validate_objects(self, value):
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            if value == "all":
                return self.queryset.all().order_by("id")
    
            if type(value) in [list, tuple]:
    
    Eliot Berriot's avatar
    Eliot Berriot committed
                return self.queryset.filter(pk__in=value).order_by("id")
    
    
            raise serializers.ValidationError(
    
    Eliot Berriot's avatar
    Eliot Berriot committed
                "{} is not a valid value for objects. You must provide either a "
                'list of identifiers or the string "all".'.format(value)
            )
    
    
        def validate(self, data):
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            dangerous = data["action"] in self.dangerous_actions
            if dangerous and self.initial_data["objects"] == "all":
    
    Eliot Berriot's avatar
    Eliot Berriot committed
                    "This action is to dangerous to be applied to all objects"
                )
            if self.filterset_class and "filters" in data:
    
                qs_filterset = self.filterset_class(
    
    Eliot Berriot's avatar
    Eliot Berriot committed
                    data["filters"], queryset=data["objects"]
                )
    
                try:
                    assert qs_filterset.form.is_valid()
                except (AssertionError, TypeError):
    
    Eliot Berriot's avatar
    Eliot Berriot committed
                    raise serializers.ValidationError("Invalid filters")
                data["objects"] = qs_filterset.qs
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            data["count"] = data["objects"].count()
            if data["count"] < 1:
                raise serializers.ValidationError("No object matching your request")
    
            return data
    
        def save(self):
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            handler_name = "handle_{}".format(self.validated_data["action"])
    
            handler = getattr(self, handler_name)
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            result = handler(self.validated_data["objects"])
    
            payload = {
    
    Eliot Berriot's avatar
    Eliot Berriot committed
                "updated": self.validated_data["count"],
                "action": self.validated_data["action"],
                "result": result,
    
            }
            return payload