Django-rest-framework: TypeError 不可散列的类型:序列化程序中带有相关字段的“dict”

创建于 2017-04-28  ·  3评论  ·  资料来源: encode/django-rest-framework

清单

  • [x] 我已经证实该问题存在于 Django REST 框架的master分支中。
  • [x] 我在打开和关闭的工单中搜索了类似的问题,但找不到重复的问题。
  • [x] 这不是一个用法问题。 (那些应该被引导到讨论组。)
  • [x] 这不能作为第三方库处理。 (我们更喜欢在可能的情况下以第三方库的形式提供新功能。)
  • [x] 我已将问题简化为最简单的情况。
  • [ ] 我已将失败的测试包含为拉取请求。 (如果您无法这样做,我们仍然可以接受该问题。)

代码

from rest_framework import serializers
from incidents.models import Incident

class NodeDataField(serializers.RelatedField):

    def to_representation(self, node):
        return {
            "id": node.id,
            "name": node.name,
            "ipaddress": node.ipv4
        }

    def to_internal_value(self, id):
        pass

这在 /api/v1/incidents/ 给出 TypeError
不可散列的类型:'dict'

unhashable type: 'dict'
5       <label class="col-sm-2 control-label {% if style.hide_label %}sr-only{% endif %}">
6         {{ field.label }}
7       </label>
8     {% endif %}
9   
10    <div class="col-sm-10">
11      <select class="form-control" name="{{ field.name }}">
12        {% if field.allow_null or field.allow_blank %}
13          <option value="" {% if not field.value %}selected{% endif %}>--------</option>
14        {% endif %}
15  

      {% for select in field.iter_options %}



16            {% if select.start_option_group %}
17              <optgroup label="{{ select.label }}">
18            {% elif select.end_option_group %}
19              </optgroup>
20            {% else %}
21              <option value="{{ select.value }}" {% if select.value|as_string == field.value|as_string %}selected{% endif %} {% if select.disabled %}disabled{% endif %}>{{ select.display_text }}</option>
22            {% endif %}
23       {% endfor %}
24     </select>

堆栈跟踪

/opt/grofers/firealarm-api/api/firealarm_api/lib/python3.4/_collections_abc.py in update
                                    self[key] = value
     ...
▶ Local vars
/opt/grofers/firealarm-api/api/firealarm_api/lib/python3.4/collections/__init__.py in __setitem__
            self.__update(*args, **kwds)
        def __setitem__(self, key, value,
                        dict_setitem=dict.__setitem__, proxy=_proxy, Link=_Link):
            'od.__setitem__(i, y) <==> od[i]=y'
            # Setting a new item creates a new link at the end of the linked list,
            # and the inherited dictionary is updated with the new key/value pair.
                        if key not in self:
     ...
                self.__map[key] = link = Link()
                root = self.__root
                last = root.prev
                link.prev, link.next, link.key = last, root, key
                last.next = link
                root.prev = proxy(link)
▼ Local vars
Variable    Value
self    
OrderedDict()
dict_setitem    
<slot wrapper '__setitem__' of 'dict' objects>
value   
'Node object'
proxy   
<built-in function proxy>
Link    
<class 'collections._Link'>
key     
{'id': 1, 'ipaddress': '172.31.84.130', 'name': 'abc123.con'}

重现步骤

  1. 对使用此相关序列化程序字段的视图进行 GET 调用,并且 /?format=api 因上述错误而失败,而 /?format=json 则按预期返回响应。

    预期行为

它应该显示 rest-framework 的可浏览 API 视图页面

实际行为

在 /api/v1/incidents/ 出现 TypeError 失败
不可散列的类型:'dict'

最有用的评论

我试图以某种方式避免覆盖序列化程序的createupdate方法。 为此,我编写了一个小类来帮助我做到这一点,但由于某种原因,我有一个与此相关的错误。

class PrimaryKeyField(serializers.RelatedField):
    def __init__(self, serializer, **kwargs):
        self.serializer = serializer(**kwargs)
        if "queryset" not in kwargs:
            kwargs["queryset"] = self.serializer.Meta.model.objects.all()
        self.pk_field = serializers.PrimaryKeyRelatedField(**kwargs)

        super().__init__(**kwargs)

    def to_representation(self, instance):
        return self.serializer.to_representation(instance)

    def to_internal_value(self, data):
        try:
            return self.pk_field.to_internal_value(data["id"])
        except TypeError:
            return data

问题是我在to_representation方法中得到了unhashable type: 'collections.OrderedDict' 。 我不明白为什么。

所有3条评论

@manjitkumar - RelatedField通常将相关对象表示为单个值(例如,slug、主键、url 等...)。 如果要提供嵌套对象表示,则应使用嵌套序列化程序

我试图以某种方式避免覆盖序列化程序的createupdate方法。 为此,我编写了一个小类来帮助我做到这一点,但由于某种原因,我有一个与此相关的错误。

class PrimaryKeyField(serializers.RelatedField):
    def __init__(self, serializer, **kwargs):
        self.serializer = serializer(**kwargs)
        if "queryset" not in kwargs:
            kwargs["queryset"] = self.serializer.Meta.model.objects.all()
        self.pk_field = serializers.PrimaryKeyRelatedField(**kwargs)

        super().__init__(**kwargs)

    def to_representation(self, instance):
        return self.serializer.to_representation(instance)

    def to_internal_value(self, data):
        try:
            return self.pk_field.to_internal_value(data["id"])
        except TypeError:
            return data

问题是我在to_representation方法中得到了unhashable type: 'collections.OrderedDict' 。 我不明白为什么。

建议一些升级?
我遇到这个错误是因为我在 to_representation() 上实现了一个dict类型的返回,所以当它试图在页面上生成表单时,它遇到了一个错误。
也许很清楚而且我很傻,但是有没有办法用现有的类来实现下面的内容?

class AnyRelatedField(serializers.RelatedField):
    def __init__(self, query_fields=tuple(), **kwargs):
        if not query_fields:
            raise NotImplementedError("AnyRelatedField requires a query field names to be provided as a tuple")

        self.query_fields = query_fields
        self.query_objects = Q()

        super().__init__(**kwargs)

    def to_internal_value(self, data):
        for field in self.query_fields:
            if field in ['pk', 'id'] and not isinstance(data, int):
                continue
            self.query_objects |= Q(**{field: data})
        try:
            self.instance = self.queryset.get(self.query_objects)
            return self.instance
        except self.queryset.model.DoesNotExist:
            raise serializers.ValidationError(
                detail="{} object does not exist with {}: {}".format(
                    self.queryset.model.__name__, ', '.join(self.query_fields), data
                ),
                code=self.field_name
            )

    def to_representation(self, val):
        return val
此页面是否有帮助?
0 / 5 - 0 等级