我需要在请求中添加一些数据,所以我做了以下事情:
data = {'my_data': 1, **request.data}
...
serializer = MySerializer(data=data)
serializer.is_valid()
但是序列化程序抱怨字段的格式不正确:
“无效的字符串。”
所有这些都表示相同。有道理,因为我看到它正在创建一个填充有列表的字典:
{'attr1':[1], 'attr2':[2], ..., 'my_data':1}
没有任何意义的是,它可以正常工作:
serializer = MySerializer(data=request.data)
serializer.is_valid()
即使QueryDict
对象也将所有字段都包装在列表中
我还尝试了以下方法:
data = {'my_data': [1], **request.data}
但是现在它也抱怨这个新领域。
我在做什么错了?
编辑:
仅需澄清一下,一种解决方法是仅解开所有项目:
data = {**{k: v for k, v in request.data.items()}}
但是,为什么序列化程序在普通字典和QueryDict
上表现不同?
答案 0 :(得分:0)
QueryDict不仅是一个以字段值作为列表的字典,而且是一个具有覆盖方法的复杂结构,这些方法定义了某些操作符的行为。以下是 MultiValueDict 中定义的getitem方法,该方法是 QueryDict 的基类。
def __getitem__(self, key):
"""
Return the last data value for this key, or [] if it's an empty list;
raise KeyError if not found.
"""
try:
list_ = super().__getitem__(key)
except KeyError:
raise MultiValueDictKeyError(key)
try:
return list_[-1]
except IndexError:
return []
使用此方法,即使键的值是列表,当您尝试从Querypct之类的
获取项目时,my_query_dict[my_key]
返回列表中的最后一项。但是,当您在查询dict上使用**构建新字典时,您得到的只是带有条目作为列表的常规字典,因此,当您获得键的值时,您会返回一个列表。举例来说,让my_query_dict是一个QueryDict实例,并保留键“ a”的值[1]。
my_query_dict['a'] # returns 1
{**my_query_dict}['a'] # returns [1]
答案 1 :(得分:0)
有人可能会说,弄混request.data
不是“正确的方法”。但是,如果仍然需要,最好创建可变的QueryDict副本:
data = request.data.copy()
data['my_data'] = 1
rest_framework
具有许多mixin / generics / builtin-views,它们仅将序列化程序初始化为SerializerCls(data=request.data, instance=something_or_none, context={'request': view.request, 'view': view}
。而且,如果您在多个视图中使用同一序列化程序,那么最好使序列化程序“智能”,而不要修改每个视图。
“智能”串行器应从“原始request.data
/ request
”中计算所有额外数据。 “部分智能”序列化程序可能需要更改视图。
如果只需要在保存时为某个模型字段设置自定义值,则可以更改视图的方法perform_update
/ perform_create
(如果使用的是GenericAPIView
-s)< / p>
def perform_update(self, serializer):
serializer.save(my_data=1)
如果您确实需要在验证过程中考虑此自定义数据:您可以使用上下文:
class MySerializer(...):
def validate(self, data):
if 'my_data' in self.context:
data['my_data'] = self.context['my_data']
if data.get('my_data'):
perform_some_extra_validation(data)
return data
serializer = MySerializer(data=data, context={'my_data': 1})
serializer.is_valid()
# for ModelView-s you can change context by modifying `get_serializer_context` method
如果您可以根据my_data
或request
动态计算request.user
,则:
serializer.validate
中进行操作:class MySerializer(...):
def validate(self, data):
if 'request' in self.context:
data['my_data'] = build_my_data_from_request(self.context['request'])
return data
serializer = MySerializer(data=data, context={'request': request})
serializer.is_valid()
# in ModelView-s serializers are already initialized with 'request' in context
default
arg的组合:
class MyDataDefault(object):
def set_context(self, serializer_field):
self.parent = serializer_field.parent
def __call__(self):
if 'request' in self.parent.context:
return build_my_data_from_request(self.parent.context['request'])
class MySerializer(...):
my_data = serializers.SomeField(default=MyDataDefault())
# if my_data should only be set during creation (not during update)
from rest_framework.serializers import CreateOnlyDefault
class AlternativeMySerializer(...):
my_data = serializers.SomeField(default=CreateOnlyDefault(MyDataDefault()))
serializer = MySerializer(data=data, context={'request': request})
serializer.is_valid()
# in ModelView-s serializers are already initialized with 'request' in context
如果您使用的是ModelViewSet
或其他基于GenericAPIView
的视图,则get_serializer
已经执行了类似于SerializerCls(data=request.data, instance=..., context={'request': request, 'view': self})
的操作:
def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
...
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}