Django rest framework 有效相关字段未被序列化程序找到但存在于请求中
Django rest framework valid related field not found by serializer but present in request
我有 2 个相关模型,我正在尝试执行 ajax 'multipart/form' post 请求。但似乎由于某种原因,有关相关模型的数据未被序列化程序识别。我尝试编辑视图集的 'create' 方法,以了解为什么数据未通过,但无济于事。我认为这个问题与 json 序列化有关,但我不知道如何解决它
以下是我尝试过的一些方法:
models.py
class Dream(models.Model):
# Some fields
...
class Milestone(models.Model):
title = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)
description = models.TextField(max_length=500)
dream = models.ForeignKey(
Dream, on_delete=models.CASCADE, related_name='milestones')
serializers.py
class DreamSerializer(TaggitSerializer, serializers.ModelSerializer):
milestones = MilestoneSerializer(many=True, read_only=False)
class Meta:
model = Dream
fields = (..., 'milestones')
class MilestoneSerializer(serializers.ModelSerializer):
class Meta:
model = Milestone
fields = ('id', 'title', 'user', 'date', 'description', 'dream')
read_only_fields = ('user', 'dream', 'date')
views.py
class DreamViewSet(viewsets.ModelViewSet):
queryset = Dream.objects.prefetch_related('user').all()
serializer_class = DreamSerializer
permission_classes = [permissions.IsAuthenticated]
# Tried to manually override the create function to fix the error
def create(self, request, *args, **kwargs):
print(request.data) # <QueryDict: {'title': ['test'], 'image':[<InMemoryUploadedFile: somePhoto.jpg (image/jpeg)>], ... , 'milestones': ['[{"title":"test1","description":"test1"}]']}>
# seems like it is evaluating to string literal '[{"title":"test1","description":"test1"}]'
print(request.data['milestones']) # [{"title":"test1","description":"test1"}]
print(type(request.data['milestones'])) # <class 'str'>
cleaned_data = request.data.copy()
milestones = cleaned_data.pop('milestones', [])
print(milestones) # ['[{"title":"test1","description":"test1"}]'] (type list)
if len(milestones):
# tried to manually deserialize data
cleaned_data['milestones'] = json.loads(milestones[0])
milestone_serializer = MilestoneSerializer(
data=cleaned_data['milestones'], many=True)
print(cleaned_data) # <QueryDict: {'title': ['test'], 'image': [<InMemoryUploadedFile: somePhoto.jpg (image/jpeg)>], 'milestones': [[{'title': 'test1', 'description': 'test1'}]]}>
print(milestone_serializer.is_valid()) # True
print(milestone_serializer.data) # [OrderedDict([('title', 'test1'), ('description', 'test1')])]
serializer = self.get_serializer(data=cleaned_data)
if serializer.is_valid(): # False
# not executedd
print(serializer.validated_data)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=201, headers=headers)
else:
print(serializer.errors) # {'milestones': [ErrorDetail(string='This field is required.', code='required')]}
在前端:
async add({title, image, ..., milestones}) {
const imageFile = new File([image], `somePhoto.jpg`, {
type: image.type,
});
const formData = new FormData();
formData.append("title", title);
formData.append("image", imageFile);
....
formData.append("milestones", JSON.stringify(milestones));
await axios.post(endpoint, formData); // status: 400, statusText: 'Bad Request', milestones: ['This field is required.']
}
它不起作用,因为在 DreamSerializer
中,您将里程碑字段设置为里程碑对象列表。但是在前端你将它设置为字符串。
你需要先改变它。
而且我觉得创建逻辑应该写在serializer的create
方法中。
首先定义其他字段用于写入里程碑数据。
import json
class DreamSerializer(TaggitSerializer, serializers.ModelSerializer):
milestones = MilestoneSerializer(many=True, read_only=True)
milestone_str = serializers.CharField(write_only = True)
class Meta:
model = Dream
fields = (..., 'milestones', 'milestone_str')
def create(self, validated_data):
milestone_str = validated_data.pop('milestone_str')
milestone_data = json.loads(milestone_str)
milestone_serializer = MilestoneSerializer(
data=milestone_data, many=True)
if milestone_serializer.is_valid():
# create Dream object first
dream = Dream.objects.create(**validated_data)
for milestone_item in milestone_data:
Milestone.objects.create(dream = dream, **milestone_item)
return dream
else:
raise serializers.ValidationError("invalid milestone data")
那么在views.py中就不需要定义create
方法了
class DreamViewSet(viewsets.ModelViewSet):
queryset = Dream.objects.prefetch_related('user').all()
serializer_class = DreamSerializer
permission_classes = [permissions.IsAuthenticated]
在前端,
async add({title, image, ..., milestones}) {
...
formData.append("milestone_str", JSON.stringify(milestones));
...
}
我有 2 个相关模型,我正在尝试执行 ajax 'multipart/form' post 请求。但似乎由于某种原因,有关相关模型的数据未被序列化程序识别。我尝试编辑视图集的 'create' 方法,以了解为什么数据未通过,但无济于事。我认为这个问题与 json 序列化有关,但我不知道如何解决它
以下是我尝试过的一些方法:
models.py
class Dream(models.Model):
# Some fields
...
class Milestone(models.Model):
title = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)
description = models.TextField(max_length=500)
dream = models.ForeignKey(
Dream, on_delete=models.CASCADE, related_name='milestones')
serializers.py
class DreamSerializer(TaggitSerializer, serializers.ModelSerializer):
milestones = MilestoneSerializer(many=True, read_only=False)
class Meta:
model = Dream
fields = (..., 'milestones')
class MilestoneSerializer(serializers.ModelSerializer):
class Meta:
model = Milestone
fields = ('id', 'title', 'user', 'date', 'description', 'dream')
read_only_fields = ('user', 'dream', 'date')
views.py
class DreamViewSet(viewsets.ModelViewSet):
queryset = Dream.objects.prefetch_related('user').all()
serializer_class = DreamSerializer
permission_classes = [permissions.IsAuthenticated]
# Tried to manually override the create function to fix the error
def create(self, request, *args, **kwargs):
print(request.data) # <QueryDict: {'title': ['test'], 'image':[<InMemoryUploadedFile: somePhoto.jpg (image/jpeg)>], ... , 'milestones': ['[{"title":"test1","description":"test1"}]']}>
# seems like it is evaluating to string literal '[{"title":"test1","description":"test1"}]'
print(request.data['milestones']) # [{"title":"test1","description":"test1"}]
print(type(request.data['milestones'])) # <class 'str'>
cleaned_data = request.data.copy()
milestones = cleaned_data.pop('milestones', [])
print(milestones) # ['[{"title":"test1","description":"test1"}]'] (type list)
if len(milestones):
# tried to manually deserialize data
cleaned_data['milestones'] = json.loads(milestones[0])
milestone_serializer = MilestoneSerializer(
data=cleaned_data['milestones'], many=True)
print(cleaned_data) # <QueryDict: {'title': ['test'], 'image': [<InMemoryUploadedFile: somePhoto.jpg (image/jpeg)>], 'milestones': [[{'title': 'test1', 'description': 'test1'}]]}>
print(milestone_serializer.is_valid()) # True
print(milestone_serializer.data) # [OrderedDict([('title', 'test1'), ('description', 'test1')])]
serializer = self.get_serializer(data=cleaned_data)
if serializer.is_valid(): # False
# not executedd
print(serializer.validated_data)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=201, headers=headers)
else:
print(serializer.errors) # {'milestones': [ErrorDetail(string='This field is required.', code='required')]}
在前端:
async add({title, image, ..., milestones}) {
const imageFile = new File([image], `somePhoto.jpg`, {
type: image.type,
});
const formData = new FormData();
formData.append("title", title);
formData.append("image", imageFile);
....
formData.append("milestones", JSON.stringify(milestones));
await axios.post(endpoint, formData); // status: 400, statusText: 'Bad Request', milestones: ['This field is required.']
}
它不起作用,因为在 DreamSerializer
中,您将里程碑字段设置为里程碑对象列表。但是在前端你将它设置为字符串。
你需要先改变它。
而且我觉得创建逻辑应该写在serializer的create
方法中。
首先定义其他字段用于写入里程碑数据。
import json
class DreamSerializer(TaggitSerializer, serializers.ModelSerializer):
milestones = MilestoneSerializer(many=True, read_only=True)
milestone_str = serializers.CharField(write_only = True)
class Meta:
model = Dream
fields = (..., 'milestones', 'milestone_str')
def create(self, validated_data):
milestone_str = validated_data.pop('milestone_str')
milestone_data = json.loads(milestone_str)
milestone_serializer = MilestoneSerializer(
data=milestone_data, many=True)
if milestone_serializer.is_valid():
# create Dream object first
dream = Dream.objects.create(**validated_data)
for milestone_item in milestone_data:
Milestone.objects.create(dream = dream, **milestone_item)
return dream
else:
raise serializers.ValidationError("invalid milestone data")
那么在views.py中就不需要定义create
方法了
class DreamViewSet(viewsets.ModelViewSet):
queryset = Dream.objects.prefetch_related('user').all()
serializer_class = DreamSerializer
permission_classes = [permissions.IsAuthenticated]
在前端,
async add({title, image, ..., milestones}) {
...
formData.append("milestone_str", JSON.stringify(milestones));
...
}