Reputation: 571
I am building an API backend with django rest framework for angular cli and I have no idea how to access verbose_name's model fields attribute in order to serialize it. This is my code:
models.py
class MyModel(model.Models):
myField = models.CharField(verbose_name='My Verbose Name')
# Here I have other 30 fields
this is the serializer for this model
serializers.py
class MyModelSerializer(ModelSerializer):
myField = SerializerMethodField()
def get_myField(self, obj):
field = next(f for f in obj._meta.fields if f.name == 'myField')
myField= {
'verbose_name': field.verbose_name.title(),
'value': obj.myField
}
return myField
# Do I Have to repeat this function for every field on my Model in order to have verbose-name ??
class Meta:
model = MyModel
fields = ['nominativo' ]
and my view is:
class MyModelListAPIView(ListAPIView):
queryset = Archivio.objects.all()
serializer_class = MyModelSerializer
My output is like i desire and is like this:
[
{
"myField":
{
"verbose_name":"My Verbose Name",
"value":"My Field value"
}
}
]
But this is okay with only few fields. In My case I need to have an output like this with 30 fields of my Model. Is there a better solution instead of making 30 get_field
functions on my ModelSerializer
class?
Upvotes: 8
Views: 4834
Reputation: 13373
You will have to override .to_representation
for your ModelSerializer
class.
Docs
Source code
You want something just like this (modified original source) -
from rest_framework import serializers
from rest_framework.relations import PKOnlyObject
class MyModelSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
"""Object instance -> Dict of primitive datatypes."""
ret = OrderedDict()
fields = self._readable_fields
for field in fields:
try:
attribute = field.get_attribute(instance)
except SkipField:
continue
# We skip `to_representation` for `None` values so that fields do
# not have to explicitly deal with that case.
#
# For related fields with `use_pk_only_optimization` we need to
# resolve the pk value.
check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
if check_for_none is None:
value = None
else:
value = field.to_representation(attribute)
ret[field.field_name] = {
'value': value,
# You can find more field attributes here
# https://github.com/encode/django-rest-framework/blob/master/rest_framework/fields.py#L324
'verbose_name': field.label,
'read_only': field.read_only,
'write_only': field.write_only,
'help_text': field.help_text,
}
return ret
Upvotes: 3
Reputation: 3922
I think playing around with the fields itself (changing representation to object) might not be the best way to achieve what you need (surely not the easiest).
Instead, I would try to define my ModelSerializer which autogenerates "labels" field, for example (basic implementation):
from rest_framework import serializers
from rest_framework.fields import SerializerMethodField
class MyModelSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super(MyModelSerializer, self).__init__(*args, **kwargs)
if 'labels' in self.fields:
raise RuntimeError(
'You cant have labels field defined '
'while using MyModelSerializer'
)
self.fields['labels'] = SerializerMethodField()
def get_labels(self, *args):
labels = {}
for field in self.Meta.model._meta.get_fields():
if field.name in self.fields:
labels[field.name] = field.verbose_name
return labels
Then, if you would use MyModelSerializer instead of serializers.ModelSerializer, you would get output like:
{
'name': 'Test',
'email': '[email protected]',
'labels': {
'name': 'Full name',
'email': 'Email address'
}
}
This way, logic for all fields generated by django rest framework stays the same and you have anohter read only field. Nice and clean.
Upvotes: 10