【问题标题】:Django Rest Framework (DRF) Refuses to Validate Data with Foreign Keys in Update (PUT) RequestDjango Rest Framework (DRF) 拒绝在更新 (PUT) 请求中使用外键验证数据
【发布时间】:2021-07-14 05:15:51
【问题描述】:

使用 Django REST Framework (DRF),我正在尝试遵循 this link 提供的嵌套序列化程序的 DRF 文档。目前,假设我的代码如下所示:

models.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

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

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__'

现在,我了解到,由于这段代码位于现在,它不是可写的序列化程序。但是,有了上面的代码,我至少不应该通过序列化程序对数据的验证吗?

使用 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."
        ]
    }
}

再次,我知道我无法像现在这样对代码执行更新 (PUT),但它至少不应该通过序列化/验证阶段吗?我也没有创建新记录。我只是试图执行更新。那么,验证为什么要关注“具有此项目编号的 pvl 条目”是否已经存在?

stackoverflow 上有很多帖子涉及或围绕这个问题展开讨论,但由于某种原因,我无法依靠其中任何一个来解决问题。

我也尝试返回并用PrimaryKeyRelatedFields 替换嵌套的序列化程序,但这种方法不会返回相关数据,只返回对相关数据的引用。

我也尝试过使用单独的序列化程序,但这种方法不适用于我为使用模板中的数据而实现的 jsGrid JavaScript。

肯定有一个简单的解决方案?

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

    第一个解决方案是您可以使用PATCH 方法而不是PUT,并且不要在表单/ajax 数据中发送pvl_project(记得设置所需的属性,如pvl_project = ProjectListSerializer(required=False)

    问题是在 drf PUT 请求方法尝试替换给定实例中所有提供的数据 - 这意味着它将首先分析没有 PvlEntry.pvl_project 属性重复因为OneToOneField.

    PATCH另一方面可以更新数据partialy(你可以分析this问题)你甚至不关心required=False序列化属性,因为它只会更新请求中提供的数据(如何通过PUT 方法更新部分在我的最后一个注释中回答)。

    第二

    现在,当我们解决了第一个概念时,我们可以继续并让这个序列化程序工作。让我们假设您在 every 模型上设置了 uuid 属性(使用 id 字段不是最佳做法)并且您希望通过给定的 uuid 属性值设置 pvl_project

    您可以覆盖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'])
    

    这个序列化器现在就像一个很好的 json api django 模型实例世界之间的弹性切换。

    【讨论】:

    • 您提供的内容对我来说很有意义。谢谢!我将暂时保持打开状态,直到明天我可以玩弄它,然后我会感谢你的答案。再次感谢!
    • 再次感谢您在这里的帮助。赞赏!
    猜你喜欢
    • 2019-04-14
    • 2017-04-09
    • 1970-01-01
    • 2017-08-15
    • 2016-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-03
    相关资源
    最近更新 更多