The Python Oracle

Django REST serializer and extra attributes from custom model fields

--------------------------------------------------
Rise to the top 3% as a developer or hire one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Puzzle Game 5 Looping

--

Chapters
00:00 Django Rest Serializer And Extra Attributes From Custom Model Fields
01:17 Accepted Answer Score 1
02:10 Answer 2 Score 0
03:47 Thank you

--

Full question
https://stackoverflow.com/questions/3021...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #django #rest #djangorestframework

#avk47



ACCEPTED ANSWER

Score 1


Question 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.

Yes, you can access your info attribute by ModelField.model_field.info. you can see the example below.

Question 2 for your final goal:

I think you can customize your own metadata 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

and, don't forget to config your DEFAULT_METADATA_CLASS settings

settings.py

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



ANSWER 2

Score 0


Ok got it, for everyone trying the same, adding extra kwargs to django-models, passing them to the rest_framework serializer, and delivering them to the Metadata scheme to get them in the OPTIONS method in a API request:

In this example i add a kwarg 'serial' to a CharField. First extend django.db.models.CharField and use it in a model:

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="")

Then create a new serializer for your new field type, eg: RsCharField below, and extend the ModelSerializer to create a mapping from the Django-model-RsCharField, to the serializer-RsCharField.

Extend the build_standard_field method of the ModelSerializer to add the extra kwargs from the django-model-RsCharField to the 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

Finally extend SimpleMetadata to pass the new kwargs to the OPTIONS method of your api, and show it in the scheme:

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

And adjust settings.py

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

Probably not neat yet, but this is the idea. Tnx soooooot!