簡體   English   中英

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

[英]How can modify request.data in django REST framework

我正在使用 Django REST 框架

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

我想添加額外的屬性,如"age": "30" ,然后再將其發送到更喜歡的地方

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

我有兩個問題

  1. 為什么 request.data 以字符串而不是字典的形式出現
  2. 我如何更新 request.data

如果您的 API 是APIView那么您應該使用更新函數來擴展您的請求數據對象,而不會丟失從客戶端發送的數據。

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

一個好朋友剛剛用比我上面說明的更簡單的方法帶我去學校

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.data應該是一個不可變的QueryDict ,而不是一個字符串。 如果需要修改:

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

您可能會檢查它是否是QueryDict的實例的唯一原因是使用常規dict更容易進行單元測試。

通常在 drf 視圖中requestrest_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

它看起來像一個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')

如果您的端點是使用 DRF 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

根據您的評論:

“因為在發布之前我需要更改 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

如果您害怕更改您的請求對象,那么使用深拷貝來復制該對象,然后您就可以輕松更改它。

用法::

from copy import deepcopy

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

隨意更改數據,因為它現在是可變的。

到目前為止,如果不使用通用視圖,這是我首選的更改方式。

對於這種方法的任何缺點,請在下面評論!

我做了以下事情:

import json

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

data['age'] = 100

現在使用可變data而不是request.data

雖然其他答案很好,但我只想在這里添加一件事;

我們現在有兩種方法來更新request.data對象但在此之前,檢查它是否是 QueryDict (正如@mikebridge 已經提到的);

from django.http.request import QueryDict


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

之后,更新request.data,第一種方法是;

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

這可以正常工作,但是如果您要更新的 request.data['key'] 是一個列表,那么new_value不會完全更改該值,但它會被附加到舊列表中,這可能會導致麻煩你可能得不到想要的結果。

所以要克服這個問題,即完全改變某個鍵的值,使用第二種方法;

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

這會將request.data['key']的值完全更改為new_value

可能在您的序列化程序中覆蓋is_valid class 將更具可讀性

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM