Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 494
Rodriguez David Avatar asked Sep 06 '17 15:09

Rodriguez David


2 Answers

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.

like image 181
marxin Avatar answered Sep 28 '22 10:09

marxin


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
like image 37
shad0w_wa1k3r Avatar answered Sep 28 '22 10:09

shad0w_wa1k3r