Django-rest-framework: TypeError unhashable type: 'dict' dengan RelatedField di Serializers

Dibuat pada 28 Apr 2017  ·  3Komentar  ·  Sumber: encode/django-rest-framework

Daftar periksa

  • [x] Saya telah memverifikasi bahwa masalah itu ada pada cabang master dari kerangka kerja Django REST.
  • [x] Saya telah mencari masalah serupa di tiket terbuka dan tertutup dan tidak dapat menemukan duplikat.
  • [x] Ini bukan pertanyaan penggunaan. (Itu harus diarahkan ke grup diskusi sebagai gantinya.)
  • [x] Ini tidak dapat ditangani sebagai perpustakaan pihak ketiga. (Kami lebih suka fungsionalitas baru dalam bentuk perpustakaan pihak ketiga jika memungkinkan.)
  • [x] Saya telah mengurangi masalah menjadi kasus yang paling sederhana.
  • [ ] Saya telah menyertakan tes yang gagal sebagai permintaan tarik. (Jika Anda tidak dapat melakukannya, kami masih dapat menerima masalah tersebut.)

Kode

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

Ini memberikan TypeError di /api/v1/incidents/
jenis yang tidak dapat dihash: '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>

Jejak tumpukan

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

Langkah-langkah untuk mereproduksi

  1. Lakukan panggilan GET ke tampilan di mana bidang Serializer Terkait ini digunakan dan /?format=api gagal dengan kesalahan yang dijelaskan di atas sedangkan /?format=json berfungsi mengembalikan respons seperti yang diharapkan.

    Perilaku yang diharapkan

Itu akan menampilkan halaman tampilan API yang dapat dijelajahi rest-framework

Perilaku sebenarnya

Gagal dengan TypeError di /api/v1/incidents/
jenis yang tidak dapat dihash: 'dict'

Komentar yang paling membantu

Saya mencoba untuk menghindari menimpa metode create dan update dari serializer di mana saya bisa. Untuk ini, saya telah menulis kelas kecil untuk membantu saya melakukan ini, tetapi untuk beberapa alasan, saya memiliki kesalahan terkait dengan yang satu ini.

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

Masalahnya adalah saya mendapatkan unhashable type: 'collections.OrderedDict' dalam metode to_representation . Dan saya tidak mengerti mengapa.

Semua 3 komentar

Hai @manjitkumar - RelatedField s umumnya mewakili objek terkait sebagai nilai tunggal (misalnya, siput, kunci utama, url, dll...). Jika Anda ingin memberikan representasi objek bersarang, maka Anda harus menggunakan serializer bersarang .

Saya mencoba untuk menghindari menimpa metode create dan update dari serializer di mana saya bisa. Untuk ini, saya telah menulis kelas kecil untuk membantu saya melakukan ini, tetapi untuk beberapa alasan, saya memiliki kesalahan terkait dengan yang satu ini.

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

Masalahnya adalah saya mendapatkan unhashable type: 'collections.OrderedDict' dalam metode to_representation . Dan saya tidak mengerti mengapa.

Sarankan beberapa peningkatan?
Saya menemukan kesalahan ini karena saya menerapkan pengembalian to_representation() yang bertipe dict jadi ketika mencoba membuat formulir pada halaman itu terjebak dengan kesalahan.
Mungkin sudah jelas dan saya konyol, tetapi apakah ada cara untuk mengimplementasikan di bawah ini dengan kelas yang ada?

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
Apakah halaman ini membantu?
0 / 5 - 0 peringkat