Django REST Framework:如何使字段的详细名称与其field_name 不同?

Bor*_*kov 6 python django django-rest-framework

我有一个带有 field 的模型tool_class,其详细名称class与 name 不同:

class Tool(models.Model):
    tool_class = jsonfield.JSONField(verbose_name="class")
Run Code Online (Sandbox Code Playgroud)

Serializer 和 ViewSet 只是库存HyperlinkedModelSerializerModelViewSet.

因此,当我使用 key 将数据 POST 或 PUT 数据发送到服务器时class,可以很好地识别:

'{"class": "..."}
Run Code Online (Sandbox Code Playgroud)

但在响应数据中它tool_class再次被调用:

{"tool_class": "..."}
Run Code Online (Sandbox Code Playgroud)

如何让它一直被调用class

我不能使用"class"字段名称的名称,因为它是python中的保留字,但在API中绝对必须调用它"class",因为API符合某个开放标准,该标准指定了这个词。

显然,我不能说:

class = CharField(source="tool_class")
Run Code Online (Sandbox Code Playgroud)

在 my 中ToolSerializer,因为它是一个SyntaxError: invalid syntax.


简单的解决方案: 另一个线程中的人提出了一个很好的解决方案。您可以使用vars()语法来规避此问题。例如,我使用以下代码:

class Tool(Document):
    vars()['class'] = mongoengine.fields.DictField(required=True)
Run Code Online (Sandbox Code Playgroud)

序列化程序自动创建相应的字段。我们不是偷偷摸摸的吗?

Bre*_*bel 2

您可以通过覆盖序列化器的元类来做到这一点。这是一个文件的示例serializers.py

主要的魔力是元类的这一部分

# Remap fields (to use class instead of class_)
fields_ = []
for name, field in fields:
    if name.endswith('_'):
        name = name.rstrip('_')
    fields_.append((name, field))
Run Code Online (Sandbox Code Playgroud)

这采用您在序列化器中定义的以下划线(即field_)结尾的任何字段,并在绑定Fields_declared_fields在序列化器上设置属性时从名称中删除下划线。

from collections import OrderedDict

from rest_framework import serializers
from rest_framework.fields import Field
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES

class MyMeta(serializers.SerializerMetaclass):

    @classmethod
    def _get_declared_fields(cls, bases, attrs):
        fields = [(field_name, attrs.pop(field_name))
                  for field_name, obj in list(attrs.items())
                  if isinstance(obj, Field)]
        fields.sort(key=lambda x: x[1]._creation_counter)

        # If this class is subclassing another Serializer, add that Serializer's
        # fields.  Note that we loop over the bases in *reverse*. This is necessary
        # in order to maintain the correct order of fields.
        for base in reversed(bases):
            if hasattr(base, '_declared_fields'):
                fields = list(base._declared_fields.items()) + fields

        # Remap fields (to use class instead of class_)
        fields_ = []
        for name, field in fields:
            if name.endswith('_'):
                name = name.rstrip('_')
            fields_.append((name, field))

        return OrderedDict(fields_)


class ToolSerializer(serializers.Serializer):

    __metaclass__ = MyMeta

    ...
    class_ = serializers.JSONField(source='tool_class', label='class')

    def create(self, validated_data):
        """
        Create and return a new `Snippet` instance, given the validated data.
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the validated data.
        """
        ...
        instance.class_ = validated_data.get('class', instance.class_)
        instance.save()
        return instance
Run Code Online (Sandbox Code Playgroud)