[英]Django Rest Framework (DRF) Refuses to Validate Data with Foreign Keys in Update (PUT) Request
Using Django REST Framework (DRF), I am trying to follow the DRF documentation for nested serializers provided by this link .使用 Django REST Framework (DRF),我试图遵循此链接提供的嵌套序列化程序的 DRF 文档。 For the moment, let's assume that my code looks like the following:
目前,让我们假设我的代码如下所示:
models.py模型.py
class PvlEntry(models.Model):
pvl_project = models.OneToOneField("review.ProjectList", on_delete=models.CASCADE, related_name='pvl_project')
pvl_reviewer = models.ForeignKey('auth.User', on_delete=models.CASCADE, related_name='+')
pvl_worktype_is_correct = models.BooleanField(blank=False, null=False)
pvl_hw_description = models.TextField(blank=False, null=False)
class ProjectList(models.Model):
"""
"""
project_number = models.IntegerField(blank=False, null=False, unique=True)
project_manager = models.CharField(blank=False, max_length=255, null=False)
project_name = models.CharField(blank=False,
max_length=255,
null=False)
project_description = models.CharField(blank=True,
max_length=1024,
null=True)
views.py视图.py
class PvlEntryListCreateAPIView(ListCreateAPIView):
""" This view is leveraged for jsGrid so that we can have jsGrid produce
a JavaScript enabled view for actions like editing and filtering of
the project vetting list.
"""
queryset = PvlEntry.objects.all()
serializer_class = PvlEntrySerializer
name = 'listcreate-pvlentry'
def get_queryset(self):
qs = self.queryset.all()
return qs
class PvlEntryRetrieveUpdateDestroyAPIView(RetrieveUpdateDestroyAPIView):
""" Leveraged for jsGrid
"""
queryset = PvlEntry.objects.all()
serializer_class = PvlEntrySerializer
name = 'rud-pvlentry'
serializers.py序列化程序.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'id',
'first_name',
'last_name',
'email'
]
class ProjectListSerializer(serializers.ModelSerializer):
class Meta:
model = ProjectList
fields = '__all__'
class PvlEntrySerializer(serializers.ModelSerializer):
pvl_project = ProjectListSerializer()
pvl_reviewer = UserSerializer()
def update(self, instance, validated_data):
print(validated_data)
return super(PvlEntrySerializer, self).update(self, instance, validated_data)
class Meta:
model = PvlEntry
fields = '__all__'
Now, I understand that as this code sits now, it's not a writeable serializer.现在,我明白,由于这段代码现在位于,它不是一个可写的序列化程序。 However, with the above code in place, should I not at least get past the serializer's validation of the data?
但是,有了上面的代码,我不应该至少通过序列化程序对数据的验证吗?
Using the DRF browsable API, when I attempt a PUT operation which is using the RetriveUpdateDestroy built-in API view, I get an error similar to the following:使用 DRF 可浏览 API,当我尝试使用 RetriveUpdateDestroy 内置 API 视图的 PUT 操作时,我收到类似于以下内容的错误:
PUT /pvl/grid/11
HTTP 400 Bad Request
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"pvl_project": {
"project_number": [
"pvl entry with this project number already exists."
]
}
}
Again, I understand that I can't perform an update (PUT) with the code as it is now, but shouldn't it at least pass the serialization/validation phase?同样,我明白我不能像现在这样使用代码执行更新 (PUT),但它至少不应该通过序列化/验证阶段吗? I'm not performing the creation of a new record, either.
我也不会创建新记录。 I'm only attempting to perform an update.
我只是尝试执行更新。 So, why should validation be concerned with whether or not a "pvl entry with this project number" already exists?
那么,为什么要验证“具有此项目编号的 pvl 条目”是否已经存在?
There are a lot of posts on stackoverflow that touch on or dance around this issue but I've not been able to lean on any of them for a resolution for some reason. stackoverflow 上有很多帖子涉及或围绕这个问题跳舞,但由于某种原因,我无法依靠其中任何一个来解决问题。
I have also tried going back and replacing the nested serializers with PrimaryKeyRelatedFields
but that approach doesn't return the related data, only references to the related data.我也尝试返回并用
PrimaryKeyRelatedFields
替换嵌套的序列化程序,但该方法不返回相关数据,只返回对相关数据的引用。
I have also experimented with separate serializers but that approach doesn't work well for the jsGrid JavaScript that I've implemented to consume the data in my template.我还尝试了单独的序列化程序,但这种方法对我实现的用于使用模板中的数据的 jsGrid JavaScript 效果不佳。
Surely there's a simple resolution?肯定有一个简单的解决方案吗?
First solution is that you can use PATCH
method instead of PUT
and don't send pvl_project
in form/ajax data (remember to set required attibute like pvl_project = ProjectListSerializer(required=False)
.第一个解决方案是您可以使用
PATCH
方法而不是PUT
并且不要在表单/ajax 数据中发送pvl_project
(记住设置所需的属性,如pvl_project = ProjectListSerializer(required=False)
。
Problem is that in drf PUT
request method tries to replace all provided data in the given instance at one - which means it will firstly analyze that there is no PvlEntry.pvl_project
property duplicates because its OneToOneField
.问题是在 drf
PUT
请求方法中尝试将给定实例中的所有提供的数据替换为一个 - 这意味着它将首先分析没有PvlEntry.pvl_project
属性重复,因为它的OneToOneField
。
PATCH
on the other hand can update data partialy
(u can analyze this problem) and you don't even care about required=False
serializer attribute because it will only update data provided in request (how to update partial by PUT
method is answered in my last annotation).另一方面,
PATCH
可以partialy
更新数据(你可以分析这个问题),你甚至不关心required=False
序列化器属性,因为它只会更新请求中提供的数据(如何通过PUT
方法更新部分在我的最后一个注释)。
Second第二
Now when we have first concept resolved we can move on and make this serializer to work.现在,当我们解决了第一个概念时,我们可以继续让这个序列化程序工作。 Lets' assume that you have uuid attribute set on every model (using
id
field is not the best practice) and you want to set pvl_project
by the given uuid
attribute value.假设您在每个模型上都设置了uuid属性(使用
id
字段不是最佳实践),并且您希望通过给定的uuid
属性值设置pvl_project
。
You can override to_internal_value
method in ProjectListSerializer
which is used on saving instance and simply search for the given object by the given data.您可以覆盖
ProjectListSerializer
中的to_internal_value
方法,该方法用于保存实例并通过给定数据简单地搜索给定对象。
class ProjectListSerializer(serializers.ModelSerializer):
class Meta:
model = ProjectList
fields = '__all__'
def to_internal_value(self, data):
# you can validate data here for sure that provided data is correct uuid string
try:
return ProjectList.objects.get(uuid=data)
except ProjectList.DoesNotExist:
raise ValidationError(['Project list with the given uuid does not exist'])
This serializer works now like a good elastic switch between json <-> api <-> django model instance world.这个序列化器现在就像一个很好的弹性切换 json <-> api <-> django 模型实例世界。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.