简体   繁体   English

Django Rest Framework (DRF) 拒绝在更新 (PUT) 请求中使用外键验证数据

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM