python - 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 中失败