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

Python 如何在django REST框架中修改request.data,python,json,django,django-rest-framework,Python,Json,Django,Django Rest Framework,我正在使用Django REST框架 request.data = '{"id": "10", "user": "tom"}' 我想添加额外的属性,比如“age”:“30”,然后再将其发送给其他类似的用户 request.data = new_data response = super().post(request, *args, **kwargs) 我有两个问题 为什么request.data是字符串而不是dict 如何更新request.data 它看起来像一个json字

我正在使用Django REST框架

request.data = '{"id": "10", "user": "tom"}'
我想添加额外的属性,比如
“age”:“30”
,然后再将其发送给其他类似的用户

    request.data = new_data
    response = super().post(request, *args, **kwargs)
我有两个问题

  • 为什么request.data是字符串而不是dict
  • 如何更新request.data

  • 它看起来像一个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')
    

    根据你的评论:

    “因为在发布之前,我需要更改API要求的字段名aqs”

    您应该使用
    字段


    这将使错误消息更加一致,否则您的用户将面临他们没有提供的字段名的错误。

    如果您的端点是用DRF
    ViewSet
    实现的,解决方案可以是实现对应的TIG序列化程序类的
    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
    

    我以不同的方式处理这个问题。我重写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
    

    一位好朋友带我去学校的方法比我上面所说的要简单得多

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

    通常,drf视图中的
    request
    rest\u framework.request.request
    实例。下面是它的源代码(
    djangorestframework==3.8.2
    ):

    @属性
    def数据(自身):
    如果不是_hasattr(self,_full_data)):
    self.\u加载\u数据和\u文件()
    返回自我。\u完整\u数据
    
    你可以做:

    request.\u full\u data=您的\u数据
    
    如果API是
    APIView
    ,则应使用更新功能扩展请求数据对象,而不会丢失从客户端发送的数据

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

    request.data
    应该是不可变的,而不是字符串。如果需要修改,请执行以下操作:

    如果存在(request.data,QueryDict):#可选
    request.data.\u mutable=True
    请求.数据['age']=“30”
    

    您可能会检查它是否是
    QueryDict
    的实例的唯一原因是,如果我使用
    request.data=json\u data
    它说无法在requestYikes上设置属性,那么使用常规的
    dict
    进行单元测试就更容易了。。。虽然这是可行的,但DRF应该有一个更干净的方法来实现这一点。请提供更多的上下文,例如如何添加额外属性以及为什么要更新request.data。请注意,更新request.data通常是个好主意。@Linovia我想添加额外的属性
    request.data['age']=30
    。现在我不能在request.data是string而不是dict时这样做。现在如果使用
    data=json.loads(request.data)
    然后
    request.data=json\u data
    那么我得到一个错误,无法在request上设置属性添加“request.data['age']=30”就是您试图做的方式,这不是你想要/需要这样做的原因。@Linovia因为在发布之前,我需要更改API所需的字段名aqs,所以我需要更改请求数据。很遗憾,没有更直观的方法来执行此操作。可能是更好的mixin或某些预定义函数。顺便说一句,如果覆盖
    ModelViewSet
    create
    update
    方法,也可以进行一些操作。