Как изменить request.data в django REST framework

Я использую Django REST Framework

request.data = '{"id": "10", "user": "tom"}'

Я хочу добавить дополнительный атрибут типа "age": "30" прежде чем отправлять его дальше

    request.data = new_data
    response = super().post(request, *args, **kwargs)

У меня есть два вопроса

  1. Почему request.data поступает как строка, а не dict
  2. Как я могу обновить request.data

Ответ 1

Это похоже на строку json. Чтобы преобразовать его в dict, вы должны:

import json
data = json.loads(request.data)

то вы можете добавить дополнительные атрибуты:

data['age'] = 30

Затем вам нужно будет сделать новый запрос, потому что кажется, что вы не можете изменить старый. Это предполагает, что вы отправляете в /notes/:

from rest_framework.test import APIRequestFactory
factory = APIRequestFactory()
request = factory.post('/notes/', data, format='json')

Ответ 2

Хороший друг привел меня в школу гораздо более простым способом, чем я иллюстрирую выше

class CreateSomething(CreateAPIView):
    model = Something
    queryset = Something.objects.all()
    serializer_class = SomethingSerializer

    perform_create(self,serializer):
    def perform_create(self,serializer):
        ip = self.get_ip()
        ## magic here: add kwargs for extra fields to write to db
        serializer.save(ip_addr=ip)

    def get_ip(self):
        x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR',None)
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = self.request.META.get('REMOTE_ADDR',None)
        return ip

class SomethingSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(validators=[UniqueValidator(queryset=Something.objects.all())])
    fieldA = serializers.CharField()
    fieldB = serializers.CharField()

    class Meta:
        model = Customer2
        fields = ['email','fieldA','fieldB','ip_addr']
        read_only_fields = ['ip_addr']

Ответ 3

Если ваша конечная точка реализована с помощью DRF ViewSet решение может состоять в том, чтобы реализовать класс to_internal_value последовательного to_internal_value и изменить данные там.

class MyModelViewSet(viewsets.ModelViewSet):
    authentication_classes = ...
    ...
    serializer_class = MyModelSerializer


class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = ('id', 'user', ...)

    def to_internal_value(self, data):
        instance = super(MyModelSerializer, self).to_internal_value(data)
        if "lastModified" in data:
            # instance["id"] = 10  # That sketchy though
            instance["user"] = "tom"
        return instance

Ответ 4

Согласно вашему комментарию:

", потому что перед публикацией мне нужно chnage имена полей aqs, требуемые API"

Вместо этого вы должны использовать аргумент source Field.

Это сделает сообщения об ошибках более последовательными, иначе ваш пользователь столкнется с ошибками с именами полей, которые они не предоставили.

Ответ 5

Я рассматривал это по-другому. Я переопределяю метод CreateAPIView create, как показано ниже.

class IPAnnotatedObject(CreateAPIView):
    model = IPAnnotatedObject
    queryset = IPAnnotatedObject.objects.all()
    serializer_class = IPAnnotatedObject
    def create(self, request, *args, **kwargs):
        request.data['ip_addr'] = self.get_ip()
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        ## perform_create calls serializer.save() which calls the serializer create() method
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def get_ip(self):
        x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR',None)
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = self.request.META.get('REMOTE_ADDR',None)
        return ip

Соответствующий класс сериализатора выглядит следующим образом

class IPAnnotatedObjectSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(validators=[UniqueValidator(queryset=IPAnnotatedObject.objects.all())])
    password = serializers.CharField(write_only=True)
    ip_addr = serializers.IPAddressField(write_only=True)
    class Meta:
        model = IPAnnotatedObject
        fields = ['email','password','created_ip']

    def create(self, validated_data):
        email, password, created_ip = validated_data['email'], validated_data['password'],validated_data['created_ip']
        try:
            ipAnnoObject = IPAnnotatedObject.objects.create(email=email,password=make_password(password),ip_addr=ip_addr)
        except Exception as e:
            # you can think of better error handler
            pass
        return ipAnnoOjbect

Ответ 6

Обычно request в представлениях rest_framework.request.Request является экземпляром rest_framework.request.Request. После этого исходный код (djangorestframework==3.8.2):

    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
        return self._full_data

Ты можешь сделать:

request._full_data = your_data