Django-rest-framework: TypeError nicht hashbarer Typ: 'dict' mit RelatedField in Serializern

Erstellt am 28. Apr. 2017  ·  3Kommentare  ·  Quelle: encode/django-rest-framework

Checkliste

  • [x] Ich habe überprüft, dass dieses Problem für den master Zweig des Django REST-Frameworks besteht.
  • [x] Ich habe sowohl in offenen als auch in geschlossenen Tickets nach ähnlichen Problemen gesucht und kann kein Duplikat finden.
  • [x] Dies ist keine Verwendungsfrage. (Diese sollten stattdessen an die Diskussionsgruppe weitergeleitet werden.)
  • [x] Dies kann nicht als Drittanbieterbibliothek behandelt werden. (Wir bevorzugen, wenn möglich, neue Funktionen in Form von Bibliotheken von Drittanbietern .)
  • [x] Ich habe das Thema auf den einfachsten Fall reduziert.
  • [ ] Ich habe einen fehlgeschlagenen Test als Pull-Request eingefügt. (Wenn Sie dazu nicht in der Lage sind, können wir das Problem trotzdem annehmen.)

Code

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

Dies gibt TypeError unter /api/v1/incidents/
Unhashable-Typ: '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>

Stapelverfolgung

/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'}

Schritte zum Reproduzieren

  1. Führen Sie einen GET-Aufruf an eine Ansicht aus, in der dieses Feld für verwandte Serialisierung verwendet wird, und /?format=api schlägt mit dem oben beschriebenen Fehler fehl, während /?format=json die Antwort wie erwartet zurückgibt.

    Erwartetes Verhalten

Es sollte die durchsuchbare API-Ansichtsseite von rest-framework anzeigen

Tatsächliches Verhalten

Fehler mit TypeError unter /api/v1/incidents/
Unhashable-Typ: 'dict'

Hilfreichster Kommentar

Ich versuche irgendwie zu vermeiden, die Methoden create und update eines Serializers zu überschreiben, wo ich kann. Dafür habe ich eine kleine Klasse geschrieben, die mir dabei hilft, aber aus irgendeinem Grund habe ich einen Fehler in diesem Zusammenhang.

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

Die Sache ist, dass ich unhashable type: 'collections.OrderedDict' in der Methode to_representation erhalte. Und ich verstehe nicht warum.

Alle 3 Kommentare

Hallo @manjitkumar - RelatedField s repräsentieren im Allgemeinen ein verwandtes Objekt als einen einzelnen Wert (zB ein Slug, Primärschlüssel, URL, etc...). Wenn Sie eine verschachtelte Objektdarstellung bereitstellen möchten, sollten Sie einen verschachtelten Serializer verwenden .

Ich versuche irgendwie zu vermeiden, die Methoden create und update eines Serializers zu überschreiben, wo ich kann. Dafür habe ich eine kleine Klasse geschrieben, die mir dabei hilft, aber aus irgendeinem Grund habe ich einen Fehler in diesem Zusammenhang.

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

Die Sache ist, dass ich unhashable type: 'collections.OrderedDict' in der Methode to_representation erhalte. Und ich verstehe nicht warum.

Einige Upgrades vorschlagen?
Ich bin auf diesen Fehler gestoßen, weil ich eine Rückgabe von to_representation() implementiert habe, die vom Typ dict Beim Versuch, das Formular auf der Seite zu generieren, wurde ein Fehler festgestellt.
Vielleicht ist es klar und ich bin albern, aber gibt es eine Möglichkeit, das Folgende mit vorhandenen Klassen zu implementieren?

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
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen