如何访问DRF中ListSerializer父类的serializer.data?

2024-01-31

我在尝试访问时遇到错误serializer.data在将其返回之前Response(serializer.data, status=something):

尝试获取字段值时出现 KeyError<field>在串行器上<serializer>.

这发生在所有字段上(因为事实证明我正在尝试访问.data在父母而不是孩子上,见下文)

类定义如下所示:

class BulkProductSerializer(serializers.ModelSerializer):

    list_serializer_class = CustomProductListSerializer

    user = serializers.CharField(source='fk_user.username', read_only=False)

    class Meta:
        model = Product
        fields = (
            'user',
            'uuid',
            'product_code',
            ...,
        )

CustomProductListSerializer is a serializers.ListSerializer并且有一个被覆盖的save()允许它正确处理批量创建和更新的方法。

这是批量产品的示例视图ViewSet:

def partial_update(self, request):

    serializer = self.get_serializer(data=request.data,
                        many=isinstance(request.data, list),
                        partial=True)
    if not serializer.is_valid():
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    serializer.save()
    pdb.set_trace()
    return Response(serializer.data, status=status.HTTP_200_OK)

正在尝试访问serializer.data跟踪(显然是后面的行)导致错误。这是完整的跟踪(tl;dr 跳过下面我用调试器进行诊断的地方):

 Traceback (most recent call last):
  File "/lib/python3.5/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/lib/python3.5/site-packages/django/core/handlers/base.py", line 249, in _legacy_get_response
    response = self._get_response(request)
  File "/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/lib/python3.5/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/lib/python3.5/site-packages/rest_framework/viewsets.py", line 86, in view
    return self.dispatch(request, *args, **kwargs)
  File "/lib/python3.5/site-packages/rest_framework/views.py", line 489, in dispatch
    response = self.handle_exception(exc)
  File "/lib/python3.5/site-packages/rest_framework/views.py", line 449, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/lib/python3.5/site-packages/rest_framework/views.py", line 486, in dispatch
    response = handler(request, *args, **kwargs)
  File "/application/siop/views/API/product.py", line 184, in partial_update
    return Response(serializer.data, status=status.HTTP_200_OK)
  File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 739, in data
    ret = super(ListSerializer, self).data
  File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 265, in data
    self._data = self.to_representation(self.validated_data)
  File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 657, in to_representation
    self.child.to_representation(item) for item in iterable
  File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 657, in <listcomp>
    self.child.to_representation(item) for item in iterable
  File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 488, in to_representation
    attribute = field.get_attribute(instance)
  File "/lib/python3.5/site-packages/rest_framework/fields.py", line 464, in get_attribute
    raise type(exc)(msg)
KeyError: "Got KeyError when attempting to get a value for field `user` on serializer `BulkProductSerializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `OrderedDict` instance.\nOriginal exception text was: 'fk_user'."

在回溯的 L657 处(来源在这里) https://github.com/encode/django-rest-framework/blob/master/rest_framework/serializers.py我有:

iterable = data.all() if isinstance(data, models.Manager) else data
return [
    self.child.to_representation(item) for item in iterable
]

这让我想知道(在跟踪中进一步挖掘)为什么 serializer.fields 不可用。我怀疑这是因为序列化器是CustomProductListSerializer父母,而不是BulkProductSerializer孩子,我是对的。在返回之前的 pdb 跟踪中Response(serializer.data):

(Pdb) serializer.fields
*** AttributeError: 'CustomProductListSerializer' object has no attribute 'fields'
(Pdb) serializer.child.fields
{'uuid': UUIDField(read_only=False, required=False, validators=[]) ...(etc)}
(Pdb) 'user' in serializer.child.fields
True
(Pdb) serializer.data
*** KeyError: "Got KeyError when attempting to get a value for field `user` on serializer `BulkProductSerializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `OrderedDict` instance.\nOriginal exception text was: 'fk_user'."
(Pdb) serializer.child.data
{'uuid': '08ec13c0-ab6c-45d4-89ab-400019874c63', ...(etc)}

好的,那么获得完整的正确方法是什么serializer.data并在以下描述的情况下在父序列化器类的响应中返回它partial_update in my ViewSet?

Edit:

class CustomProductListSerializer(serializers.ListSerializer):

    def save(self):
        instances = []
        result = []
        pdb.set_trace()
        for obj in self.validated_data:
            uuid = obj.get('uuid', None)
            if uuid:
                instance = get_object_or_404(Product, uuid=uuid)
                # Specify which fields to update, otherwise save() tries to SQL SET all fields.
                # Gotcha: remove the primary key, because update_fields will throw exception.
                # see https://stackoverflow.com/a/45494046
                update_fields = [k for k,v in obj.items() if k != 'uuid']
                for k, v in obj.items():
                    if k != 'uuid':
                        setattr(instance, k, v)
                instance.save(update_fields=update_fields)
                result.append(instance)
            else:
                instances.append(Product(**obj))

        if len(instances) > 0:
            Product.objects.bulk_create(instances)
            result += instances

        return result

正如评论中提到的,我仍然认为例外可能是因为中的用户字段批量产品序列化器类,实际上没有任何关系ListSerializer

如文档中所述,序列化器 DRF 中可能存在另一个小错误(但很重要)here http://www.django-rest-framework.org/api-guide/serializers/#customizing-listserializer-behavior。以下是如何指定一个list_serializer_class:

class CustomListSerializer(serializers.ListSerializer):
    ...

class CustomSerializer(serializers.Serializer):
    ...
    class Meta:
        list_serializer_class = CustomListSerializer

请注意,它是在内部指定的Meta课堂上,而不是外面。所以我认为在你的代码中,它不会理解切换到列表序列化器many=True。这应该会导致不更新的问题。

更新 - 添加更新嵌套列表序列化器的示例

看来问题更多的是关于实现嵌套列表序列化器更新的通用方法,而不是实际的错误。因此,我将尝试提供示例代码。

一些注意事项:

  • 如果我们使用模型视图集,列表路由不允许PUT or PATCH,所以两者都不是update nor 部分更新将被称为(参考 http://www.django-rest-framework.org/api-guide/routers/#defaultrouter)。因此我使用POST直接,这样就简单多了。
  • 如果你想使用PUT/PATCH,然后看这个答案here https://stackoverflow.com/a/27871396/1149484
  • 我们总是可以添加一个查询参数,例如允许更新 or partial直接发送Post请求来区分POST/PUT/PATCH
  • 而不是使用uuid就像这个问题我会使用普通的id,应该是非常一样的

本来很简单

作为参考,模型如下所示:

class Product(models.Model):
    name = models.CharField(max_length=200)
    user = models.ForeignKey(User, null=True, blank=True)

    def __unicode__(self):
        return self.name

第 1 步:确保序列化器更改为列表序列化器

class ProductViewSet(viewsets.ModelViewSet):
    serializer_class = ProductSerializer
    queryset = Product.objects.all()

    def get_serializer(self, *args, **kwargs):
        # checking for post only so that 'get' won't be affected
        if self.request.method.lower() == 'post':
            data = kwargs.get('data')
            kwargs['many'] = isinstance(data, list)
        return super(ProductViewSet, self).get_serializer(*args, **kwargs)

第 2 步:实施列表序列化器通过覆盖create功能

class ProductListSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        new_products = [Product(**p) for p in validated_data if not p.get('id')]
        updating_data = {p.get('id'): p for p in validated_data if p.get('id')}
        # query old products
        old_products = Product.objects.filter(id__in=updating_data.keys())
        with transaction.atomic():
            # create new products
            all_products = Product.objects.bulk_create(new_products)
            # update old products
            for p in old_products:
                data = updating_data.get(p.id, {})
                # pop id to remove
                data.pop('id')
                updated_p = Product(id=p.id, **data)
                updated_p.save()
                all_products.append(updated_p)
        return all_products


class ProductSerializer(serializers.ModelSerializer):
    user = serializers.SlugRelatedField(slug_field='username', queryset=User.objects.all())
    id = serializers.IntegerField(required=False)

    class Meta:
        model = Product
        fields = '__all__'
        list_serializer_class = ProductListSerializer
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何访问DRF中ListSerializer父类的serializer.data? 的相关文章

  • SQLAlchemy 通过关联对象声明式多对多自连接

    我有一个用户表和一个朋友表 它将用户映射到其他用户 因为每个用户可以有很多朋友 这个关系显然是对称的 如果用户A是用户B的朋友 那么用户B也是用户A的朋友 我只存储这个关系一次 除了两个用户 ID 之外 Friends 表还有其他字段 因此
  • Flask 会话变量

    我正在用 Flask 编写一个小型网络应用程序 当两个用户 在同一网络下 尝试使用应用程序时 我遇到会话变量问题 这是代码 import os from flask import Flask request render template
  • django.db.utils.ProgrammingError:关系“django_content_type”不存在

    我有一个项目 我在我的电脑上慢慢建立起来 并且运行良好 我只是想将它放到服务器上 但收到此错误 django db utils ProgrammingError 关系 django content type 确实 不存在 我无法解决这个问题
  • Python pickle:腌制对象不等于源对象

    我认为这是预期的行为 但想检查一下 也许找出原因 因为我所做的研究结果是空白 我有一个函数可以提取数据 创建自定义类的新实例 然后将其附加到列表中 该类仅包含变量 然后 我使用协议 2 作为二进制文件将该列表腌制到文件中 稍后我重新运行脚本
  • OpenCV 无法从 MacBook Pro iSight 捕获

    几天后 我无法再从 opencv 应用程序内部打开我的 iSight 相机 cap cv2 VideoCapture 0 返回 并且cap isOpened 回报true 然而 cap grab 刚刚返回false 有任何想法吗 示例代码
  • Python 函数可以从作用域之外赋予新属性吗?

    我不知道你可以这样做 def tom print tom s locals locals def dick z print z name z name z guest Harry print z guest z guest print di
  • AWS EMR Spark Python 日志记录

    我正在 AWS EMR 上运行一个非常简单的 Spark 作业 但似乎无法从我的脚本中获取任何日志输出 我尝试过打印到 stderr from pyspark import SparkContext import sys if name m
  • 绘制方程

    我正在尝试创建一个函数 它将绘制我告诉它的任何公式 import numpy as np import matplotlib pyplot as plt def graph formula x range x np array x rang
  • 从 Flask 访问 Heroku 变量

    我已经使用以下命令在 Heroku 配置中设置了数据库变量 heroku config add server xxx xxx xxx xxx heroku config add user userName heroku config add
  • Django admin - 更改权限列表

    是否可以更改用户编辑页面中的权限列表 我不想显示所有权限 例如管理日志条目或身份验证组等 如何修改主查询集以排除其中的某些查询集 我的想法来自这个话题 https groups google com forum topic django u
  • IO 密集型任务中的 Python 多线程

    建议仅在 IO 密集型任务中使用 Python 多线程 因为 Python 有一个全局解释器锁 GIL 只允许一个线程持有 Python 解释器的控制权 然而 多线程对于 IO 密集型操作有意义吗 https stackoverflow c
  • 使用 \r 并打印一些文本后如何清除控制台中的一行?

    对于我当前的项目 有一些代码很慢并且我无法使其更快 为了获得一些关于已完成 必须完成多少的反馈 我创建了一个进度片段 您可以在下面看到 当你看到最后一行时 sys stdout write r100 80 n I use 80覆盖最终剩余的
  • 如何在Python中对类别进行加权随机抽样

    给定一个元组列表 其中每个元组都包含一个概率和一个项目 我想根据其概率对项目进行采样 例如 给出列表 3 a 4 b 3 c 我想在 40 的时间内对 b 进行采样 在 python 中执行此操作的规范方法是什么 我查看了 random 模
  • 向 Altair 图表添加背景实心填充

    I like Altair a lot for making graphs in Python As a tribute I wanted to regenerate the Economist graph s in Mistakes we
  • 为字典中的一个键附加多个值[重复]

    这个问题在这里已经有答案了 我是 python 新手 我有每年的年份和值列表 我想要做的是检查字典中是否已存在该年份 如果存在 则将该值附加到特定键的值列表中 例如 我有一个年份列表 并且每年都有一个值 2010 2 2009 4 1989
  • Python 类继承 - 诡异的动作

    我观察到类继承有一个奇怪的效果 对于我正在处理的项目 我正在创建一个类来充当另一个模块的类的包装器 我正在使用第 3 方 aeidon 模块 用于操作字幕文件 但问题可能不太具体 以下是您通常如何使用该模块 project aeidon P
  • 导入错误:没有名为 site 的模块 - mac

    我已经有这个问题几个月了 每次我想获取一个新的 python 包并使用它时 我都会在终端中收到此错误 ImportError No module named site 我不知道为什么会出现这个错误 实际上 我无法使用任何新软件包 因为每次我
  • Python Selenium:如何在文本文件中打印网站上的值?

    我正在尝试编写一个脚本 该脚本将从 tulsaspca org 网站获取以下 6 个值并将其打印在 txt 文件中 最终输出应该是 905 4896 7105 23194 1004 42000 放置的动物 的 HTML span class
  • NotImplementedError:无法将符号张量 (lstm_2/strided_slice:0) 转换为 numpy 数组。时间

    张量流版本 2 3 1 numpy 版本 1 20 在代码下面 define model model Sequential model add LSTM 50 activation relu input shape n steps n fe
  • Statsmodels.formula.api OLS不显示截距的统计值

    我正在运行以下源代码 import statsmodels formula api as sm Add one column of ones for the intercept term X np append arr np ones 50

随机推荐