1

I'm in a situation where I have a endpoint samples which represents a model sample via a ModelViewSet.

My goal is, that when a user POST's against this endpoint with data

like

{
    "a":1,
    "b":2 ,
    "c":3
}

i want to be able to override/add key:value pairs to this incoming payload stored in request.data within the create method.

This can not be done by simply accessing request.data since it's a QueryDict which is immutable.

Furthermore i can not achieve this in the perform_create() method since the data i might inject is validation-critical.

Currently I'm stuck with the following solution which requires me to REWRITE the complete create() method :

class MyViewSet(viewsets.ModelViewSet):
    queryset = Sample.objects.all()
    serializer_class = MSampleSerializer
    name = "samples"

    def add_info(self, request):
        ...
        <acquire_info>
        ...
        data = request.data.dict()
        data["s"] = <info1>
        data["r"] = <info1>
        data["t"] = <info1>
        return data


    def create(self, request, *args, **kwargs):
        data = self.add_info(request)
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(
            serializer.data, status=status.HTTP_201_CREATED, headers=headers
        )
  1. Is there a generic way to edit the request.data before any action method, like create() or put(), is called ?

  2. If not 1.); is there a different possibility ?

Thanks in advance.

Chgad
  • 820
  • 2
  • 7
  • 18
  • Wouldn't it be better for you to do this in a django middleware instead? https://simpleisbetterthancomplex.com/tutorial/2016/07/18/how-to-create-a-custom-django-middleware.html – Jason Feb 16 '19 at 20:01

1 Answers1

2

You can use the .to_representation() or .to_internal_value() methods of a serializer if you don't want to rewrite the whole view every time.

And since you want to validate the data too, .to_representation() might be the way to go about it.

From the docs:

If you need to alter the serialization or deserialization behavior of a serializer class, you can do so by overriding the .to_representation() or .to_internal_value() methods.

.to_representation(self, obj) takes the object instance that requires serialization, and should return a primitive representation. Typically this means returning a structure of built-in Python datatypes. The exact types that can be handled will depend on the render classes you have configured for your API.

Example:

def to_representation(self, instance):
    """Convert `username` to lowercase."""
    ret = super().to_representation(instance)
    ret['username'] = ret['username'].lower()
    return ret

See Advanced serializer usage: Overriding serialization and deserialization behavior

Community
  • 1
  • 1
mehamasum
  • 5,554
  • 2
  • 21
  • 30
  • I tried your suggestion, but it seems that i cannot access the request object within `to_internal_value()` which i need to do in this case. – Chgad Feb 17 '19 at 15:32
  • Do you need the request object to access `request.data`? I think you can get them in `instance` or `ret` (from example). Let's try printing those two. – mehamasum Feb 17 '19 at 15:35
  • Pls excuse me, my answer was a bit to hasty. I was just copy-pasting my code from the Viewset into the serializer. The `request`object is stored in `self.context["request"]` within the serializer which did the trick. To your comment : No i do not need the request object for acces `data` or `instance` but for the ` ` part which i did not specify in my original question. – Chgad Feb 17 '19 at 15:42