Rodriguez David
Rodriguez David

Reputation: 571

Django verbose_name attribute on serializers

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

Answers (2)

shad0w_wa1k3r
shad0w_wa1k3r

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

marxin
marxin

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

Related Questions