Django REST 序列化程序和来自自定义模型字段的额外属性

Django REST serializer and extra attributes from custom model fields

如何将自定义模型字段中的额外属性传递给序列化程序?

例如,我有这个自定义模型字段 RsTestField,它有一个额外的属性 "info",它是 True 或 False:

class RsTestField(models.Field):

    __metaclass__ = models.SubfieldBase

    def get_internal_type(self):
        return "CharField"

    def __init__(self, info=False, *args, **kwargs):
        self.info = info
        super(RsTestField, self).__init__(*args, **kwargs)

    def is_info(self):
        return self.info

在以下模型中使用,我可以在其中传递此自定义属性的值:

class Client(models.Model):

    test1 = RsTestField(max_length=255, info=True, default="")
    name1 = models.CharField(max_length=255, default="")

以及以下序列化程序:

class ClientSerializer(serializers.HyperlinkedModelSerializer):

    test1 = ModelField(model_field=Client()._meta.get_field('test1'))

    class Meta:
        model = Client
        fields = ('name1','test1')

我希望能够像访问 name1-max_length 属性一样访问 test1-info 属性。

这可能吗?

目标是最终在 Scheme 概述中传递此属性,可以使用 OPTIONS http 请求检索该属性:

"actions": {
    "POST": {
        "name1": {
            "type": "string",
            "required": false,
            "read_only": false,
            "label": "Client name 1",
            "max_length": 255
        },
        "test1": {
            "type": "field",
            "required": true,
            "read_only": false,
            "label": "Test1"
        }
    }
}

在"test1"中应该有一个额外的键:

"info": True

问题 1:

I want to be able to access the test1-info attribute just like i would be able to access the name1-max_length attribute.

是的,您可以通过 ModelField.model_field.info 访问您的 info 属性。 你可以看下面的例子。

问题 2 的最终目标:

我认为您可以自定义自己的元数据class。

from rest_framework.metadata import SimpleMetadata
from rest_framework.serializers import ModelField
from pbweb.models import RsTestField


class MyMetadata(SimpleMetadata):
    def get_field_info(self, field):
        field_info = super(MyMetadata, self).get_field_info(field)
        # I will add the info field only for RsTestField-ModelField
        if isinstance(field, ModelField) and isinstance(field.model_field, RsTestField):
            # access your info attribute HERE
            field_info['info'] = field.model_field.info
        return field_info

并且,不要忘记配置您的 DEFAULT_METADATA_CLASS 设置

settings.py

REST_FRAMEWORK = {
    'DEFAULT_METADATA_CLASS': 'my.customize.MyMetadata'
}

好的,对于每个尝试相同的人来说,向 django-models 添加额外的 kwargs,将它们传递给 rest_framework 序列化程序,并将它们传递给元数据方案以在 OPTIONS 方法中获取它们API 请求:

在这个例子中,我向 CharField 添加了一个 kwarg 'serial'。首先扩展 django.db.models.CharField 并在模型中使用它:

models.py

class RsCharField(models.CharField, metaclass=models.SubfieldBase):

    def __init__(self, serial=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.serial = serial


class MyModel(models.Model):

    fied_name1 = RsCharField(max_length=100, serial=123456, default="")

然后为您的新字段类型创建一个新的序列化程序,例如:下面的 RsCharField,并扩展 ModelSerializer 以创建从 Django-model-RsCharField 到序列化程序-RsCharField 的映射。

扩展 ModelSerializer 的 build_standard_field 方法以将额外的 kwargs 从 django-model-RsCharField 添加到 serializers-RsCharField

serializers.py

from rest_framework import serializers

class RsCharField(serializers.CharField):

    def __init__(self, serial=None, **kwargs):
        self.serial = serial
        super().__init__(**kwargs)


class RsModelSerializer(serializers.ModelSerializer):

    serializer_field_mapping = serializers.ModelSerializer.serializer_field_mapping
    serializer_field_mapping[myapp.models.RsCharField] = RsCharField

    def build_standard_field(self, field_name, model_field):
        field_class, kwargs =  super().build_standard_field(field_name, model_field)

        if isinstance(model_field, kernel.fields.RsCharField):
            kwargs['serial'] = model_field.serial

        return field_class, kwargs

最后扩展 SimpleMetadata 以将新的 kwargs 传递给 api 的 OPTIONS 方法,并在方案中显示它:

class RsMetaData(SimpleMetadata):

    def get_field_info(self, field):

        field_info = super(RsMetaData, self).get_field_info(field)
        if(isinstance(field, RsCharField)):
            field_info['serial'] = field.serial

        return field_info

并调整settings.py

REST_FRAMEWORK = {
    'DEFAULT_METADATA_CLASS': 'my.customize.RsMetaData'
}

可能还不完善,但就是这个想法。 Tnx 太棒了!