Reputation: 2711
How can i pass extra attributes from my custom model field to the serializer?
For example i have this custom model field RsTestField which has an extra attribute "info" which is True or 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
Which is used in the following model, where i can pass the value of this custom attribute:
class Client(models.Model):
test1 = RsTestField(max_length=255, info=True, default="")
name1 = models.CharField(max_length=255, default="")
And the following serializer:
class ClientSerializer(serializers.HyperlinkedModelSerializer):
test1 = ModelField(model_field=Client()._meta.get_field('test1'))
class Meta:
model = Client
fields = ('name1','test1')
I want to be able to access the test1-info attribute just like i would be able to access the name1-max_length attribute.
Is this possible?
The goal is to eventually pass this attribute in the Scheme overview which can be retrieved with the OPTIONS http request:
"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"
}
}
}
In "test1" there should come an extra key:
"info": True
Upvotes: 4
Views: 5859
Reputation: 2711
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!
Upvotes: 0
Reputation: 1759
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'
}
Upvotes: 1