首页 > 解决方案 > DRF Browsable API - 确保在将字典传递给 GET 响应时它可以工作

问题描述

我有一个应用程序,当用户在/inventory/item_mast/. 用户应该得到的响应是:

{
   pk:1,
   item_name: "laptop"
   item_cat_name: {id:1,name:"techonology"}
}

使用我当前的实现,我得到了所需的响应,但 Django Rest Framework 可浏览 API 不起作用

REST API 工作并且 GET 请求能够接收所需的数据,但是我不能使用 django-rest-framework 提供的 Django Web Api,因为它不期望 JSON 对象中任何键的值是字典。它返回错误

TypeError at /inventory/item_mast/
unhashable type: 'dict'

如何仍然使用 dict 作为值来访问 Web API

全栈跟踪:

Environment:


Request Method: GET
Request URL: http://localhost:8000/inventory/item_mast/

Django Version: 3.1.2
Python Version: 3.7.7
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'hr',
 'inventory',
 'company_setup',
 'oauth2',
 'corsheaders',
 'rest_framework',
 'oauth2_provider',
 'django_extensions',
 'silk']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'silk.middleware.SilkyMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'oauth2_provider.middleware.OAuth2TokenMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'sips_backend.middleware.CreatedModifiedMiddleware']


Template error:
In template /home/admin/anaconda3/lib/python3.7/site-packages/rest_framework/templates/rest_framework/horizontal/select.html, error at line 15
   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>
   25 : 


Traceback (most recent call last):
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/core/handlers/base.py", line 202, in _get_response
    response = response.render()
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/response.py", line 105, in render
    self.content = self.rendered_content
  File "/home/admin/anaconda3/lib/python3.7/site-packages/rest_framework/response.py", line 70, in rendered_content
    ret = renderer.render(self.data, accepted_media_type, context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/rest_framework/renderers.py", line 724, in render
    context = self.get_context(data, accepted_media_type, renderer_context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/rest_framework/renderers.py", line 696, in get_context
    'post_form': self.get_rendered_html_form(data, view, 'POST', request),
  File "/home/admin/anaconda3/lib/python3.7/site-packages/rest_framework/renderers.py", line 511, in get_rendered_html_form
    return self.render_form_for_serializer(serializer)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/rest_framework/renderers.py", line 521, in render_form_for_serializer
    {'style': {'template_pack': 'rest_framework/horizontal'}}
  File "/home/admin/anaconda3/lib/python3.7/site-packages/rest_framework/renderers.py", line 372, in render
    return template.render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/backends/django.py", line 61, in render
    return self.template.render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 170, in render
    return self._render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 162, in _render
    return self.nodelist.render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/defaulttags.py", line 211, in render
    nodelist.append(node.render_annotated(context))
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/defaulttags.py", line 312, in render
    return nodelist.render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/library.py", line 192, in render
    output = self.func(*resolved_args, **resolved_kwargs)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/rest_framework/templatetags/rest_framework.py", line 87, in render_field
    return renderer.render_field(field, style)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/rest_framework/renderers.py", line 351, in render_field
    return template.render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/backends/django.py", line 61, in render
    return self.template.render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 170, in render
    return self._render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 162, in _render
    return self.nodelist.render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/defaulttags.py", line 163, in render
    values = self.sequence.resolve(context, ignore_failures=True)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 671, in resolve
    obj = self.var.resolve(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 796, in resolve
    value = self._resolve_lookup(context)
  File "/home/admin/anaconda3/lib/python3.7/site-packages/django/template/base.py", line 858, in _resolve_lookup
    current = current()
  File "/home/admin/anaconda3/lib/python3.7/site-packages/rest_framework/relations.py", line 220, in iter_options
    self.get_choices(cutoff=self.html_cutoff),
  File "/home/admin/anaconda3/lib/python3.7/site-packages/rest_framework/relations.py", line 207, in get_choices
    for item in queryset

Exception Type: TypeError at /inventory/item_mast/
Exception Value: unhashable type: 'dict'

我的代码如下models.py

class Item_Mast(AbstractBaseModel):
    pk = models.AutoField(primary_key=True)
    item_name = models.CharField(max_length=50)
    item_cat_mast = models.ForeignKey("inventory.Item_Cat_Mast",  on_delete=models.SET_NULL, null=True)

    def __str__(self):
        return self.item_name

序列化程序.py

class Item_Mast_Serializer(serializers.ModelSerializer):
    item_cat_mast = IdNameField(id_field='pk',name_field='item_cat_name', queryset=Item_Cat_Mast.objects.all())
    class Meta:
        model = Item_Mast
        fields = '__all__'

我编写了一个自定义序列化程序,以 {id:"some_id",name:"some_name"} 格式返回 ForeignKey 字段的数据。它的代码是:

class IdNameField(serializers.RelatedField):

    def __init__(self, *args, **kwargs):
        id_field = kwargs.pop('id_field', None)
        name_field = kwargs.pop('name_field', None)
        
        super(IdNameField, self).__init__(*args, **kwargs)
        self.id_field = id_field
        self.name_field = name_field        

    def to_representation(self, obj):
        return {
            'id': getattr(obj,self.id_field),
            'name': getattr(obj,self.name_field),
        }

    def to_internal_value(self, data):
        try:
            if(type(data) == dict):
                try:
                    return self.get_queryset().get(**{self.id_field:data["id"]})
                except KeyError:
                    raise serializers.ValidationError('id is a required field.')
                except ValueError:
                    raise serializers.ValidationError('id must be an integer.')
            else: 
                return self.get_queryset().get(**{self.id_field:data})
        except:
            raise serializers.ValidationError('Obj does not exist.')

视图.py

-----------------QUESTION__CHANGES__AS__提到__IN__COMMENT-------------------

class Item_Mast_ListView(GenericAPIView):
    queryset = Item_Mast.objects.all().select_related("item_cat_mast")
    serializer_class = Item_Mast_Serializer

    def get(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        serializer = self.get_serializer(queryset,many=True)
        return Response(serializer.data)

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)

如果我从 ListView 中删除 def post 函数,一切正常,即使是 DRF 可浏览 API,否则如果我不删除它,除了可浏览 API 之外的所有内容仍然有效。

我不明白为什么定义 post 函数会导致 GET 请求在可浏览 API 中失败

标签: pythondjangodjango-rest-frameworkdjango-views

解决方案


推荐阅读