How to access extra data sent with POST request in

2019-08-07 00:01发布

问题:

I'm trying to send an extra array with a POST request that creates a group.

POST /groups/
{
  "name": “New Group“,
  "users_to_invite": [ 1, 2, 6 ]
}

I'm using Django Rest Framework's ListCreateAPIView to handle the request:

class GroupListView(ListCreateAPIView):
    queryset = Group.objects.all()

    def get_serializer(self, *args, **kwargs):
        kwargs['context'] = self.get_serializer_context()

        # Use a dedicated serializer to inspect the field with users to invite
        if self.request.method == 'POST':
            return GroupCreateSerializer(*args, **kwargs)
        return GroupSerializer(*args, **kwargs)

I want to get to the contents of the users_to_invite array in a serializer to create invitation objects. However, I'm unable to do so because somehow the array is not there when I'm accessing it in the serializer.

class GroupCreateSerializer(ModelSerializer):

    def create(self, validated_data):
        # Get user ids to invite
        # !! It fails here !!
        invitations_data = validated_data.pop('users_to_invite')

        # TODO: Iterate over ids and create invitations

        # Create a group
        group = Group.objects.create(**validated_data)
        return group

    class Meta:
        model = Group
        fields = ('id', 'name', )

Array is not there so naturally I'm getting the error:

File "...my_api/serializers.py", line 303, in create
    invitations_data = validated_data.pop('users_to_invite')
KeyError: 'users_to_invite'

How can I make the array to appear there? Modifying the model does not sound like a good idea because I don't want to store what's in the array.

回答1:

To send extra context data you should pass it from the method def get_serializer_context(self) and access inside serializer like self.context.get("key").

sample code:

class GroupListView(ListCreateAPIView):
    queryset = Group.objects.all()

    def get_serializer_context(self):
        context = super(GroupListView, self).get_serializer_context()
        context["users_to_invite"] = request.data.get("users_to_invite")
        return context

class GroupCreateSerializer(ModelSerializer):

    def create(self, validated_data):
        users_to_invite = self.context.get("users_to_invite")
        # do whatever you want

Reference: https://micropyramid.com/blog/django-rest-framework-send-extra-context-data-to-serializers/



回答2:

extra data is absent in validated_data you can try use self.context['request'] details: including-extra-context, by default it added in the get_serializer method of view

for your case:

def create(self, validated_data):
    # Get user ids to invite
    # !! It fails here !!
    request = self.context['request']
    invitations_data = request.data.get('users_to_invite')


回答3:

Apart from adding the data to context, you can add the field users_to_invite to serializer as a CharField.

Example:

import json

class GroupCreateSerializer(ModelSerializer):

    def create(self, validated_data):
        # Get user ids to invite
        # !! It fails here !!
        invitations_data = json.loads(validated_data.pop('users_to_invite'))

        # TODO: Iterate over ids and create invitations

        # Create a group
        group = Group.objects.create(**validated_data)
        return group

    users_to_invite = serializers.CharField(required=False)

    class Meta:
        model = Group
        fields = ('id', 'name', 'users_to_invite', )

Note: Make sure to have users_to_invite in request data as a string. Like: '[1, 2, 3]'