首页 文章

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

提问于
浏览
4

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

例如,我有这个自定义模型字段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')

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

这可能吗?

目标是最终在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

2 回答

  • 0

    问题1:

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

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

    问题2为您的最终目标:

    我认为您可以自定义自己的元数据类 .

    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'
    }
    
  • 1

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

    在这个例子中,我将一个kwarg'serial'添加到CharField . 首先扩展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到serializer-RsCharField的映射 .

    扩展ModelSerializer的build_standard_field方法,将django-model-RsCharField中的额外kwargs添加到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 soooooot!

相关问题