【问题标题】:How to serialize an 'object list' in Django REST Framework如何在 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

标签: json django django-rest-framework


【解决方案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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-07-12
        • 2014-02-09
        • 1970-01-01
        • 2013-05-23
        • 1970-01-01
        • 1970-01-01
        • 2016-10-22
        • 2019-03-16
        相关资源
        最近更新 更多