【问题标题】:django rest framework: How to not allow validation on a nested serializer data?django rest 框架:如何不允许对嵌套的序列化程序数据进行验证?
【发布时间】:2018-09-23 11:30:05
【问题描述】:

我有一个模型 Book 的序列化程序 BookSerializer 和模型 Page 的嵌套 PageSerializer。更新Book 实例涉及删除其所有Page 实例,然后创建这些Page 实例,可能有或没有涉及页面的一些新数据或没有或现有数据的更新。

但是我的Book 模型有一个约束,每个Book 实例只能有最大100 页。验证检查在PageSerializervalidate 方法内完成。我有另一个理由在这里这样做:

def validate(self, attrs):
    #some logic here
    raise some error if book instance already has 100 pages or if it will have more than 100 pages when combined with new pages when added.

序列化器的更新方法定义或者说BookSerializer是:

def update(self, instance, validated_data):
    ...
    ...

因此,发送到更新的数据已经过验证,可以在 update 方法中以 validated_data 的形式访问。

现在,问题来了。嵌套序列化程序PageSerializer 有一个validate 方法,该方法检查该Book 实例的多少页已经存在并针对它们进行验证。

B 成为Book 的一个实例,该实例已经拥有100 页面。如果我尝试更新 B 而不进行任何更改,发送的数据仍会在 BookSerializer 中进行验证,将其页面数据传递给 PageSerializer,然后最后将 validated_data 传递给 update(self, instance, val;idated_data) 方法。

我的问题是:

  • 如何在验证发送更新B的页面数据之前删除B的所有页面?
  • 或者如何绕过嵌套序列化程序的验证?仅验证序列化程序,并在保存之前负责验证父序列化程序的更新方法内的嵌套序列化程序。

【问题讨论】:

    标签: python django django-models django-rest-framework


    【解决方案1】:

    我建议你删除 BookSerializer 的 update(**kwargs) 方法中的页面。在PageSerializer 的验证方法中,您应该验证接收到的数据(您不应将现有页面视为一个序列,您将删除所有页面)。这是仅完全覆盖现有书籍页面的情况。但是,当您想更新它们时,您还应该返回页面的id(您应该在PageSerializer 中添加id,以便嵌套序列化程序可以识别您正在更新实例)。所以需要验证才能更新。

    请求正文应该是这样的:

    {
       'book_title': 'TITLE'
       'pages':[
               {'id': 3, 'title': 'TITLE', 'body': 'BODY'},
               {'title': 'TITLE', 'body': 'BODY'}, ...
        ]
        ....
    }
    class PageSerializer(serializers.ModelSerializer):
        class Meta:
            model = Page
            fields =['id', .....]
    
    class Book(serializers.ModelSerializer):
        pages = PageSerializer(many=True)
    
        class Meta:
            model = Book
            fields = ['id', 'pages', ....]
    
        def validate_pages(self, values): # here values will be as [{...},{...},..]
            obj_list = list()
            for value in values:
                pk = value.pop('id', None)
                if pk:
                   try:
                      page = Page.objects.get(pk=pk)
                      for k, v in value.items():
                          setattr(page, key, value)
                      obj_list.append(page)
                   except Page.DoesNotExists():
                      # handle by yourself, skip or raise error
                else:
                    obj_list.append(Page(**value))
             # here you can validate length also
             return obj_list
    

    create(self, validated_data)update(self, instance, validated_data) 中,您将页面作为对象列表。所以你可以使用这个删除:

    pages = validated_data['pages']
    page_ids = [page.pk for page in pages if page.pk] # it get all ids from list
    if page_ids:
       Page.objects.filter(book=instance).exclude(pk__in=page_ids).delete() it can be applicable only for updating
    

    以及使用批量或迭代的简单创建。

    【讨论】:

    • 好的。如果Book 有 100 个页面,我将页面 1-9,20-50 传递给更新,并且包含要创建的没有 id 的新页面。这会删除页面10-19, 51-100吗?并创建新页面?
    • 您可以将validate_pages(self, value) 添加到您的BookSerializer。在这种方法中,您可以操作您的值并返回它。
    • 您正在将 obj_list 重新创建为之前的形式。请看一下。这不起作用,因为验证的顺序是nested_serializer 然后parent_serializer。因此,混合了 id 且没有页面 id 的数据首先传递给PageSerializer,它会识别缺少的 id 或任何其他验证,并在到达您的 validate_pages 之前抛出错误。
    • 在这里您应该将验证移到上层(从PageSerializerBookSerializer)。此外,我认为(我没有为我证明)嵌套序列化器验证将首先工作,而不是父序列化器验证工作。不过,如果您将长度验证添加到 serializers.CharField 并添加自定义方法验证长度验证比验证函数更有效,那么我会得出这个结论。
    • 上层是什么意思? PageSerilizer 验证(验证_ 然后验证)发生在 BookSeriallizer 验证(验证_ 然后验证)之前。无论是自定义字段还是其他任何内容。
    猜你喜欢
    • 1970-01-01
    • 2018-07-06
    • 2019-12-23
    • 2021-06-23
    • 2016-05-14
    • 2018-12-13
    • 2018-07-07
    • 2016-05-31
    • 1970-01-01
    相关资源
    最近更新 更多