我正在使用Django REST框架
request.data = '{"id": "10", "user": "tom"}'
我想添加额外的属性,例如"age": "30"
,然后再将其发送到进一步的类似
request.data = new_data
response = super().post(request, *args, **kwargs)
我有两个问题
- 为什么request.data以字符串而不是字典的形式出现
- 如何更新请求数据
如果您的 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">
你应该改用Field
的source
参数。
这将使错误消息更加一致,否则您的用户将遇到未提供的字段名称的错误。
我以不同的方式处理这个问题。我覆盖了 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)