Django-rest-framework: TypeErrorハッシュ不可能なタイプ: 'dict'とSerializersのRelatedField

作成日 2017年04月28日  ·  3コメント  ·  ソース: encode/django-rest-framework

チェックリスト

  • [x]その問題がDjangoRESTフレームワークの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'

最も参考になるコメント

シリアライザーのcreate updateメソッドと

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通常、関連するオブジェクトを単一の値(たとえば、スラッグ、主キー、URLなど)として表します。 ネストされたオブジェクト表現を提供する場合は、ネストされ

シリアライザーのcreate updateメソッドと

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'を取得しているということです。 そして、私はそれをなぜ理解していません。

いくつかのアップグレードを提案しますか?
dictタイプのto_representation()にreturnを実装していたため、このエラーが発生しました。そのため、ページでフォームを生成しようとすると、エラーが発生しました。
おそらくそれは明らかで、私はばかげていますが、既存のクラスで以下を実装する方法はありますか?

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 評価