如何在 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 用户密码?

如何仅使用 django 作为后端并使用 django-rest-framework 发布

如何在 React 中显示来自 django-rest-framework 的错误消息