如何在 Django REST Framework 中序列化“对象列表”
Posted
技术标签:
【中文标题】如何在 Django REST Framework 中序列化“对象列表”【英文标题】:How to serialize an 'object list' in Django REST Framework 【发布时间】:2015-03-15 00:55:46 【问题描述】:我需要一个序列化器来处理这样的事情:
"items":
12:
"name": "item 1"
,
66:
"name": "item 2"
我应该如何声明我的序列化程序以获得这样的东西?这甚至是一个有效的 JSON 还是应该看起来像这样:
"items": [
"name": "item 1",
"id": 12
,
"name": "item 2"
"id": 66
]
? (12、66 是这些“项目”的主键) 使用 Django REST 框架 3。
【问题讨论】:
第一个不是有效的 json。在这里查看jsonlint。对象key
必须始终是字符串。所以12
至少应该是"12"
。第二个很容易生产。发布你的模型,我会帮忙的。
选择我的答案是正确的:p
【参考方案1】:
不需要序列化器。您可以使用基于类的视图做的更简单:
from rest_framework import views
from rest_framework.response import Response
from django.http import JsonResponse
class ListItems(views.APIView):
def get(self, request, format=None):
items = Item.objects.all().values()
return JsonResponse(list(items), safe=False)
还有safe=False
,永远记住。
【讨论】:
【参考方案2】:这样做的那种列表,在文档中是dicts
,但在数据库中是lists
,在你的例子中你使用整数作为键,你需要使用字符串来遵守JSON标准。
from collections import OrderedDict
from django.core.exceptions import FieldDoesNotExist
from django.db import models as django_models
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.fields import SkipField
from rest_framework.settings import api_settings
from rest_framework.utils import html
class ObjectListSerializer(serializers.ListSerializer):
child = None
many = True
default_error_messages =
'not_a_dict': _('Expected a dict of items but got type "input_type".'),
'empty': _('This dict may not be empty.')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not hasattr(self, 'index_field') or not self.index_field:
if 'index_field' in kwargs:
index_field = kwargs.pop('index_field')
if index_field in self.child.fields:
self.index_field = index_field
else:
raise FieldDoesNotExist(
_("Field field_name does not exists in serializer_name.").format(
field_name=index_field,
serializer_name=self.child.__class__.__name__
)
)
else:
index_field = None
serializer_model = self.child.get_model()
if serializer_model:
try:
index_field = serializer_model._meta.get_field('pk')
except FieldDoesNotExist:
try:
index_field = serializer_model._meta.get_field('id')
except FieldDoesNotExist:
pass
if index_field:
self.index_field = index_field.name
else:
raise FieldDoesNotExist(
_(
"Cannot find primary key in serializer_name, "
"try the argument 'index_field' in my_name."
).format(
my_name=self.__class__.__name__,
serializer_name=self.child.__class__.__name__
)
)
if not hasattr(self, 'index_field') or not self.index_field:
raise FieldDoesNotExist(
_("Provide the 'index_field' argument for serializer_name,").format(
serializer_name=self.__class__.__name__
)
)
def get_initial(self):
if hasattr(self, 'initial_data'):
return self.to_representation(self.initial_data)
return
def to_internal_value(self, data):
"""
List of dicts of native values <- List of dicts of primitive datatypes.
"""
if html.is_html_input(data):
data = html.parse_html_list(data, default=[])
if not isinstance(data, dict):
message = self.error_messages['not_a_dict'].format(
input_type=type(data).__name__
)
raise ValidationError(
api_settings.NON_FIELD_ERRORS_KEY: [message]
, code='not_a_list')
if not self.allow_empty and len(data) == 0:
if self.parent and self.partial:
raise SkipField()
message = self.error_messages['empty']
raise ValidationError(
api_settings.NON_FIELD_ERRORS_KEY: [message]
, code='empty')
ret = []
errors = []
for index_value, item in data.items():
item[self.index_field] = index_value
try:
validated = self.child.run_validation(item)
except ValidationError as exc:
errors.append(exc.detail)
else:
ret.append(validated)
errors.append()
if any(errors):
raise ValidationError(errors)
return ret
def to_representation(self, data):
"""
List of object instances -> List of dicts of primitive datatypes.
"""
# Dealing with nested relationships, data can be a Manager,
# so, first get a queryset from the Manager if needed
iterable = data.all() if isinstance(data, django_models.Manager) else data
ret = OrderedDict()
for item in iterable:
dict_doc = self.child.to_representation(item)
ret[dict_doc.pop(self.index_field)] = dict_doc
return ret
您可以在类的初始化处使用参数index_field
来使用此序列化程序
class ItemSerializer(serializers.Serializer):
name = serializers.CharField(max_length=64)
description = serializers.CharField()
class BucketSerializer(Serializer):
items = ObjectListSerializer(
child=ItemSerializer,
index_field='name',
allow_empty=True
)
或者,如果您想用作list_serializer_class
,则使用index_field
预定义类值扩展类
class ItemsListSerializer(ObjectListSerializer):
index_field = 'name'
allow_empty = True
class ItemSerializer(serializers.Serializer):
name = serializers.CharField(max_length=64)
description = serializers.CharField()
class Meta:
list_serializer_class = ItemListSerializer
class BucketSerializer(serializers.Serializer):
items = ItemSerializer(many=True, required=False)
【讨论】:
【参考方案3】:django rest framework 3中有一个ListField,你可以在这里查看文档http://www.django-rest-framework.org/api-guide/fields/#listfield
对于您的示例,您可以执行以下操作:
class ItemSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
class ItemsSerializer(serializers.Serializer):
items = serializers.ListField(child=ItemSerializer())
后面的序列化器也可以是:
class ItemsSerializer(serializers.Serializer):
items = ItemSerializer(many=True)
【讨论】:
以上是关于如何在 Django REST Framework 中序列化“对象列表”的主要内容,如果未能解决你的问题,请参考以下文章
Django.rest_framework:如何序列化一对多?
如何使用 TemplateHTMLRenderer 在 Django-REST-Framework 中创建/放置?
django-rest-framework:如何序列化已经包含 JSON 的字段?
如何在 Django Rest Framework 中散列 Django 用户密码?