Django-rest-framework: TypeError tipo inalterável: 'dict' com RelatedField em serializadores

Criado em 28 abr. 2017  ·  3Comentários  ·  Fonte: encode/django-rest-framework

Lista de controle

  • [x] Eu verifiquei que esse problema existe no branch master da estrutura REST do Django.
  • [x] Pesquisei problemas semelhantes em tíquetes abertos e fechados e não consigo encontrar uma duplicata.
  • [x] Esta não é uma questão de uso. (Em vez disso, eles devem ser direcionados ao grupo de discussão .)
  • [x] Isso não pode ser tratado como uma biblioteca de terceiros. (Preferimos que a nova funcionalidade esteja na forma de bibliotecas de terceiros, sempre que possível.)
  • [x] Reduzi o problema ao caso mais simples possível.
  • [] Incluí um teste com falha como uma solicitação de pull. (Se você não puder fazer isso, ainda podemos aceitar o problema.)

Código

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

Isso dá TypeError em / api / v1 / incidents /
tipo inalterável: '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>

Rastreamento de pilha

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

Passos para reproduzir

  1. Faça uma chamada GET para uma visualização em que este campo do serializador relacionado está sendo usado e /? Format = api falha com o erro descrito acima, enquanto /? Format = json funciona retorna a resposta conforme o esperado.

    Comportamento esperado

Ele deve exibir a página de visualização da API navegável do rest-framework

Comportamento real

Falha com TypeError em / api / v1 / incidents /
tipo inalterável: 'dict'

Comentários muito úteis

Estou tentando, de alguma forma, evitar sobrescrever os métodos create e update de um serializador onde posso. Para isso, escrevi uma pequena aula para me ajudar a fazer isso, mas por algum motivo, tenho um erro relacionado a este.

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

O fato é que estou obtendo unhashable type: 'collections.OrderedDict' no método to_representation . E eu não entendo por quê.

Todos 3 comentários

Olá @manjitkumar - RelatedField s geralmente representam um objeto relacionado como um valor único (por exemplo, um slug, chave primária, url, etc ...). Se quiser fornecer uma representação de objeto aninhado, você deve usar um serializador aninhado .

Estou tentando, de alguma forma, evitar sobrescrever os métodos create e update de um serializador onde posso. Para isso, escrevi uma pequena aula para me ajudar a fazer isso, mas por algum motivo, tenho um erro relacionado a este.

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

O fato é que estou obtendo unhashable type: 'collections.OrderedDict' no método to_representation . E eu não entendo por quê.

Sugerir algumas atualizações?
Encontrei este erro porque estava implementando um return em to_representation () que era do tipo dict então, quando ele tentou gerar o formulário na página, encontrou um erro.
Talvez esteja claro e eu seja bobo, mas existe uma maneira de implementar o abaixo com as classes existentes?

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
Esta página foi útil?
0 / 5 - 0 avaliações