Django-rest-framework: TypeError unhashable type: 'dict' avec RelatedField dans les sérialiseurs

Créé le 28 avr. 2017  ·  3Commentaires  ·  Source: encode/django-rest-framework

Liste de contrôle

  • [x] J'ai vérifié que ce problème existe avec la branche master du framework Django REST.
  • [x] J'ai recherché des problèmes similaires dans les tickets ouverts et fermés et je ne trouve pas de doublon.
  • [x] Ce n'est pas une question d'utilisation. (Ceux-ci devraient plutôt être dirigés vers le groupe de discussion .)
  • [x] Cela ne peut pas être traité comme une bibliothèque tierce. (Nous préférons que les nouvelles fonctionnalités se présentent sous la forme de bibliothèques tierces dans la mesure du possible.)
  • [x] J'ai réduit le problème au cas le plus simple possible.
  • [ ] J'ai inclus un test d'échec en tant que demande d'extraction. (Si vous ne pouvez pas le faire, nous pouvons toujours accepter le problème.)

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

Cela donne TypeError à /api/v1/incidents/
type non illisible : '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>

Trace de la pile

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

Étapes à reproduire

  1. Effectuez un appel GET à une vue dans laquelle ce champ Related Serializer est utilisé et /?format=api échoue avec l'erreur décrite ci-dessus alors que /?format=json fonctionne renvoie la réponse comme prévu.

    Comportement prévisible

Il devrait afficher la page d'affichage de l'API navigable de rest-framework

Comportement réel

Échec avec TypeError dans /api/v1/incidents/
type non illisible : 'dict'

Commentaire le plus utile

J'essaie d'éviter d'écraser les méthodes create et update d'un sérialiseur là où je le peux. Pour cela, j'ai écrit une petite classe pour m'aider à le faire, mais pour une raison quelconque, j'ai une erreur liée à celle-ci.

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

Le fait est que j'obtiens unhashable type: 'collections.OrderedDict' dans la méthode to_representation . Et je ne comprends pas pourquoi.

Tous les 3 commentaires

Salut @manjitkumar - RelatedField s représentent généralement un objet connexe comme une valeur unique (par exemple, un slug, une clé primaire, une URL, etc...). Si vous souhaitez fournir une représentation d'objet imbriquée, vous devez utiliser un sérialiseur imbriqué .

J'essaie d'éviter d'écraser les méthodes create et update d'un sérialiseur là où je le peux. Pour cela, j'ai écrit une petite classe pour m'aider à le faire, mais pour une raison quelconque, j'ai une erreur liée à celle-ci.

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

Le fait est que j'obtiens unhashable type: 'collections.OrderedDict' dans la méthode to_representation . Et je ne comprends pas pourquoi.

Suggérer des améliorations ?
Je suis tombé sur cette erreur parce que j'implémentais un retour sur to_representation() qui était de type dict donc quand il a essayé de générer le formulaire sur la page, il s'est retrouvé avec une erreur.
C'est peut-être clair et je suis idiot, mais y a-t-il un moyen d'implémenter ce qui suit avec les classes existantes ?

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
Cette page vous a été utile?
0 / 5 - 0 notes