如何在 django REST 框架中修改 request.data



我正在使用Django REST框架

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

我想添加额外的属性,例如"age": "30",然后再将其发送到进一步的类似

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

我有两个问题

  1. 为什么request.data以字符串而不是字典的形式出现
  2. 如何更新请求数据

如果您的 API APIView那么您应该使用更新函数来扩展您的请求数据对象,而不会丢失从客户端发送的数据。

request.data.update({"id": "10", "user": "tom"})

>request.data应该是一个不可变的QueryDict,而不是一个字符串。 如果需要修改它:

if isinstance(request.data, QueryDict): # optional
    request.data._mutable = True
request.data['age'] = "30"

您可以检查它是否是QueryDict实例的唯一原因是,使用常规dict更容易进行单元测试。

一个好朋友带我去上学,方法比我上面说明的要简单得多

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']

通常request drf 视图中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

如果你的端点是用DRF(Django REST框架(实现的,ViewSet解决方案可以是实现相应的序列化程序类的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's sketchy though
            instance["user"] = "tom"
        return instance

它看起来像一个 json 字符串。要将其转换为字典,您应该执行以下操作:

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')

如果您害怕更改请求对象,请使用深拷贝来复制对象,然后可以轻松更改它。

用法::

from copy import deepcopy
# here is your other code and stuffs
data = deepcopy(request.data)

随意更改数据,因为它现在是可变的。

到目前为止,如果不

使用通用视图,这是我首选的更改方式。

对于此方法的任何缺点,请在下面评论!

虽然其他答案很好,但我只想在这里补充一件事;

我们现在有两种方法来更新request.data对象 但在此之前,请检查它是否是 QueryDict(如 @mikebridge 所述(;

from django.http.request import QueryDict

if isInstance(request.data, QueryDict):
    request.data._mutable = True

之后,要更新请求数据,第一种方法是;

request.data.update({'key': 'new_value'})

这将正常工作,但是假设您要更新的request.data['key']是一个列表,那么该值不会被new_value完全更改,但它会被附加到旧列表中,这可能会导致麻烦,并且您可能无法获得所需的结果。

因此,要克服这一点,即完全更改某个键的值,请使用第二种方法;

request.data['key'] = 'new_value'

这会将request.data['key']的值完全更改为 new_value

根据您的评论:

"因为在发布之前,我需要更改 API 所需的字段名称 aqs">

你应该改用Fieldsource参数。

这将使错误消息更加一致,否则您的用户将遇到未提供的字段名称的错误。

我以不同的方式处理这个问题。我覆盖了 CreateAPIView 创建方法,如下所示

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's 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

我做了以下操作:

import json
data = json.dumps(request.data)
data = json.loads(data)
data['age'] = 100

现在使用变量data而不是request.data

可能会被覆盖is_valid在你的序列化程序类中会更易读

class YourSerializer
    
    # Don't forget it
    age = serializers.IntegerField()
    def is_valid(self, raise_exception: bool = ...) -> bool:
        self.initial_data["age"] = 30
        return super().is_valid(raise_exception)

最新更新