Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DRF: static/read-only/non-model fields in serializers?

Is it possible to include static fields in a model serializer? For example, I have a model:

class Location(models.Model):
    long = models.FloatField(...)
    lat = models.FloatField(...)

and a serializer:

class LocationSerialiser(serializers.ModelSerializer):
    class Meta:
        model = Location
        fields = ("id", "long", "lat")

with a simple ListAPIView I can see all my locations:

{
    'id': 1,
    'long': ...,
    'lat': ...,
}, 
...

but say I want to have a static, non-model field added to all objects in my json response? Something like:

{
    'id': 1,
    'long': ...,
    'lat': ...,
    'display': True,
    'icon': 'image/marker.png'
}, 
...

How can I add these to the serializer? I've looked through the fields module (for 2.2.4) but I don't see any clear way of achieving this. Everything needs a source attribute.

Ideally I would like to do

class LocationSerialiser(serializers.ModelSerializer):
    display = fields.BooleanField(value=True)
    icon = fields.CharField(value="image/marker.png")

    class Meta:
        model = Location
        fields = ("id", "long", "lat")

EDIT

One approach I've found is to simply put extra @property wrapped methods on the model instance:

class Location(models.Model):
    long = models.FloatField(...)
    lat = models.FloatField(...)

    @property
    def show(self):
        return True

    ...

and then in the serializer:

class LocationSerialiser(serializers.ModelSerializer):
    display = fields.BooleanField(value=True)
    icon = fields.CharField(value="image/marker.png")

    show = fields.CharField(source="show")

    class Meta:
        model = Location
        fields = ("id", "long", "lat")

but this seems very hacky. There must be a cleaner way of achieving this?

like image 447
Timmy O'Mahony Avatar asked Nov 20 '14 18:11

Timmy O'Mahony


1 Answers

If this field is a property or method on your model (like get_full_name, for instance), you can have a custom source on serializer fields that points to them. Django REST Framework will automatically detect that they are methods, and will still call and display them.

class Location(models.Model):
    # ...

    def display(self):
        return True

    def icon(self):
        return "image/marker.png"

class LocationSerialiser(serializers.ModelSerializer):
    display = serializers.BooleanField(read_only=True)
    icon = serializers.CharField(read_only=True)

    class Meta:
        model = Location
        fields = ("id", "long", "lat", "display", "icon", )

If you can't or don't want to put the function on your model, Django REST Framework has a SerializerMethodField that will call a method on your serializer that can return data that will be displayed.

class LocationSerialiser(serializers.ModelSerializer):
    display = serializers.SerializerMethodField("get_display")
    icon = serializers.SerializerMethodField("get_icon")

    class Meta:
        model = Location
        fields = ("id", "long", "lat", "display", "icon", )

    def get_display(self, obj):
        return True

    def get_icon(self, obj):
        return "image/marker.png"
like image 51
Kevin Brown-Silva Avatar answered Sep 29 '22 20:09

Kevin Brown-Silva