[英]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)
我有兩個問題
如果您的 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 視圖中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
它看起來像一個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
我以不同的方式處理了這個問題。 我重寫了 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.