如何将ListSerializer与ModelSerializer一起使用?

时间:2019-08-20 21:28:32

标签: django python-3.x django-rest-framework django-views django-rest-viewsets

我正在尝试使用DRF ListSerializer创建POST端点以创建LogLevel对象的列表。

我尝试使用PrimaryKeyRelatedField序列化外键,但没有成功。

models.py
relevant fields for LogLevel model. note foreign key to node model
    #associated node
    node = models.ForeignKey(Node, on_delete=models.DO_NOTHING,
                             related_name="log_levels")
    #logger name
    name = models.CharField(max_length=32, choices=LOGGERS)
    # Current log level
    level = models.IntegerField(default=INFO,
                                choices=LOG_LEVELS)
    # Timestamps
    created_datetime = models.DateTimeField(auto_now_add=True)
    updated_datetime = models.DateTimeField(auto_now=True,
                                            blank=True, null=True)

serializers.py
class LogLevelListSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        log_levels = [LogLevel(**item) for item in validated_data]
        levels = LogLevel.objects.bulk_create(log_levels)
        return levels


class LogLevelCreateUpdateSerializer(serializers.ModelSerializer):
    class Meta:
        model = LogLevel
        fields = "__all__"
        list_serializer_class = LogLevelListSerializer
LogLevel view
class LogLevelList(MethodSerializerMixin,
                   generics.ListCreateAPIView):
    """
    Log Level list API Endpoint.
    """
    method_serializer_classes = {
        ("POST",): LogLevelCreateUpdateSerializer
    }

    def get_queryset(self):
        """
        Queryset to use for endpoint.
        """
        return LogLevel.objects.all()

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        # check if many is required
        if "data" in kwargs:
            data = kwargs["data"]

            # check if many is required
            if isinstance(data, list):
                kwargs["many"] = True

        return serializer_class(*args, **kwargs)

MethodSerializerMixin

from rest_framework import exceptions


class MethodSerializerMixin(object):
    """
    Utility class to apply a different serializer class depending
    on the request method.

    For example:

    method_serializer_classes = {
        ("GET", ): MyModelListViewSerializer,
        ("PUT", "PATCH"): MyModelCreateUpdateSerializer
    }
    """
    method_serializer_classes = None

    def get_serializer_class(self):
        assert self.method_serializer_classes is not None, (
            f"Expected view {self.__class__.__name__} should contain "
            f"method_serializer_classes to get right serializer class."
        )
        for methods, serializer_cls in self.method_serializer_classes.items():
            if self.request.method in methods:
                return serializer_cls

        raise exceptions.MethodNotAllowed(self.request.method)

我在请求中传递了简单对象的json列表。节点是外键ID:

[{
    "name": "logger1",
    "level": 2,
    "node": 1
},
{
    "name": "logger2",
    "level": 3,
    "node": 1
}]

我希望对象可以创建并以成功状态显示给客户端。当前,对象已在数据库中成功创建,但是返回了500: Server Error,这是我在Django服务器上看到的堆栈跟踪:

Internal Server Error: /api/clustering/loglevel/set/
Traceback (most recent call last):
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/django/core/handlers/base.py", line 145, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/django/core/handlers/base.py", line 143, in _get_response
    response = response.render()
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/django/template/response.py", line 106, in render
    self.content = self.rendered_content
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/response.py", line 72, in rendered_content
    ret = renderer.render(self.data, accepted_media_type, context)
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/renderers.py", line 724, in render
    context = self.get_context(data, accepted_media_type, renderer_context)
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/renderers.py", line 697, in get_context
    'post_form': self.get_rendered_html_form(data, view, 'POST', request),
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/renderers.py", line 520, in get_rendered_html_form
    return self.render_form_for_serializer(serializer)
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/renderers.py", line 528, in render_form_for_serializer
    serializer.data,
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/serializers.py", line 765, in data
    ret = super(ListSerializer, self).data
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/serializers.py", line 266, in data
    self._data = self.get_initial()
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/serializers.py", line 600, in get_initial
    return self.to_representation(self.initial_data)
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/serializers.py", line 683, in to_representation
    self.child.to_representation(item) for item in iterable
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/serializers.py", line 683, in <listcomp>
    self.child.to_representation(item) for item in iterable
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/serializers.py", line 527, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "/opt/cisco/env/iris/lib/python3.6/site-packages/rest_framework/relations.py", line 257, in to_representation
    return value.pk
AttributeError: 'int' object has no attribute 'pk'
python==3.6
django==2.2.2
drf==3.8.2

2 个答案:

答案 0 :(得分:0)

serializer.data属性仅在将实例保存到序列化程序时才有效。 在保存之前调用serializer.save()或使用serializer.validated_data访问数据。

查看此link以获得更多信息。

答案 1 :(得分:0)

必须通过更新PrimaryKeyRelatedField类上的to_representation方法来处理此错误

class NodePrimaryKeyField(serializers.PrimaryKeyRelatedField):
    """
    Custom DRF serializer field for proper handling of
    Node Foreign Key by ListSerializer on validation error
    """
    def to_representation(self, value):
        """
        Return pk value of serialized Node object
        if available else return given ID value
        """
        if self.pk_field is not None:
            return self.pk_field.to_representation(value.pk)
        return getattr(value, 'pk', value)