How to change field name in Django REST Framework
Asked Answered
P

5

160

I am trying to change Model field name in DRF Serializer like alias in SQL. I have tried different methods but cannot succeed.

models.py

class Park(models.Model):
    name = models.CharField(max_length=256)
    alternate_name = models.CharField(max_length=256, blank=True)
    objects = models.GeoManager()

    class Meta:
        db_table = u'p_park'

    def __unicode__(self):
        return '%s' % self.name

    def alias_alternate_name(self):
        return self.alternate_name

serializers.py

class ParkSerializer(serializers.ModelSerializer):

    location = serializers.Field(source='alias_alternate_name')
    #location = serializers.SerializerMethodField(source='alias_alternate_name')

    #alternate_name as location


    class Meta:
        model = Park
        fields = ('id', 'name', 'location')

I have also tried to add alias in Django Queryset but cannot changed.

Updated

This is the exception that i am facing

AttributeError at /ViewName/ 'module' object has no attribute 'Field'

How can I do this?

Postwar answered 9/4, 2014 at 9:19 Comment(3)
Are you using a correct implementation of the serializers.SerializerMethodField approach? I mean this: serializers.SerializerMethodField('get_location') and def get_location(self, obj): ...Ankledeep
Can we see the imports of serializers.py?Lobster
will downvote question because OP accepted a partially wrong and confusing answer instead of the better ones below...Ailment
A
97

You can use serializers.SerializerMethodField:

Here is the model Park, which has name and alternate_name fields.

class Park(models.Model):
    name = models.CharField(max_length=256)
    alternate_name = models.CharField(max_length=256, blank=True)
    objects = models.GeoManager()

    class Meta:
        db_table = u'p_park'

    def __unicode__(self):
        return '%s' % self.name

Here is Serializer for Park Model, ParkSerializer. This changes the name of alternate_name to location.

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.SerializerMethodField('get_alternate_name')

    class Meta:
        model = Park
        fields = ('other_fields', 'location')

    def get_alternate_name(self, obj):
        return obj.alternate_name

Additionally, you can use serializers.CharField with source attribute:

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.CharField(source='other_fields')

    class Meta:
        model = Park
        fields = ('other_fields', 'location')

Django's __ notation to traverse foreign key also works:

location = serializers.CharField(source='OtherModel__other_fields')

The same principle applies if you want to change the return type on the API, so you can do serializers.DecimalField(source=...) and other field types as well.

This would however work only for read only fields.

Ankledeep answered 9/4, 2014 at 9:49 Comment(10)
Now this exception is throwing AttributeError at /ViewName/ 'module' object has no attribute 'SerializerMethodField'Postwar
It's a clashing of the names serializers.py at the your project, and serialisers at the rest_framework. Try this: from rest_framework import serializers as rest_serializers and location = rest_serializers.SerializerMethodField('get_alternate_name')Ankledeep
How would this workout with create and edit requests?Lifesaver
As far as I understand, it will not affect the creation or updating, because it's a read-only field (see the doc)Ankledeep
Exactly, the question never limits the requirement to get. He wants the field name to be changed for all type of requests.Lifesaver
the question never limits the requirement to get - Why do you think so? From what I see in the code there is an attempt to use a function result (def alias_alternate_name()) as a serializer field and nothing else.Ankledeep
Line no 13 of 'Zen of Python' : "There should be one -- and preferably only one --obvious way to do it."Lifesaver
This should not be the accepted answer. See the one below, that has almost 5 times as many upvotes at the time of me writing this.Dunbar
This is a bad solution. Use the source kwarg instead as described below.Ezana
class ParkSerializer(serializers.ModelSerializer): location = serializers.CharField(source='alternate_name', allow_null=True) class Meta: model = Park fields = ('other_fields', 'location')Parlormaid
L
304

There is a very nice feature in serializer fields and serializers in general called 'source' where you can specify source of data from the model field.

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.SomeSerializerField(source='alternate_name')

    class Meta:
        model = Park
        fields = ('other_fields', 'location')

Where serializers.SomeSerializerField can be serializers.CharField as your model suggests but can also be any of the other fields. Also, you can put relational fields and other serializers instead and this would still work like charm. ie even if alternate_name was a foreignkey field to another model.

class ParkSerializer(serializers.ModelSerializer):
    locations = AlternateNameSerializer(source='alternate_name', many=true)

    class Meta:
        model = Park
        fields = ('other_fields', 'locations')

class AlternateNameSerializer(serializers.ModelSerialzer):
    class Meta:
        model = SomeModel

This works with the creation, deletion, and modification requests too. It effectively creates one on one mapping of the field name in the serializer and field name in models.

Lifesaver answered 24/10, 2015 at 21:35 Comment(7)
I agreed, that source is more general approach. But you can see few attempts to use it in the question, so if you would like to answer that way you also should make clear why original code is not working, isn't it?Ankledeep
Your code will work fine.. as long as the request is for list and retrieveLifesaver
Both answers are incomplete. In the case of foreign key, this method implies that when creating a new Park, you have to give the whole parent object (alternate_name) as a dict in your POST request, which is insane since the parent object already exists. One should be able to mention the foreign instance via its id.Horvath
In my case (foreign key) I solved this problem with locations = serializers.PrimaryKeyRelatedField(source='alternate_name', queryset=AlternateName.objects.all()). Apparently RelatedField can be used as well.Horvath
@chefarov source='new_name' is a generic argument you can give to serializer fields, relations and other related serializers etc.. Not sure why you say the answer is incomplete.Lifesaver
I followed this but in the update() method (and maybe the create() method, the validated_data key is alternate_name not locations. Any advice?Morph
Replace SomeSerializerField with any of the fields from django-rest-framework.org/api-guide/fieldsBolger
A
97

You can use serializers.SerializerMethodField:

Here is the model Park, which has name and alternate_name fields.

class Park(models.Model):
    name = models.CharField(max_length=256)
    alternate_name = models.CharField(max_length=256, blank=True)
    objects = models.GeoManager()

    class Meta:
        db_table = u'p_park'

    def __unicode__(self):
        return '%s' % self.name

Here is Serializer for Park Model, ParkSerializer. This changes the name of alternate_name to location.

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.SerializerMethodField('get_alternate_name')

    class Meta:
        model = Park
        fields = ('other_fields', 'location')

    def get_alternate_name(self, obj):
        return obj.alternate_name

Additionally, you can use serializers.CharField with source attribute:

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.CharField(source='other_fields')

    class Meta:
        model = Park
        fields = ('other_fields', 'location')

Django's __ notation to traverse foreign key also works:

location = serializers.CharField(source='OtherModel__other_fields')

The same principle applies if you want to change the return type on the API, so you can do serializers.DecimalField(source=...) and other field types as well.

This would however work only for read only fields.

Ankledeep answered 9/4, 2014 at 9:49 Comment(10)
Now this exception is throwing AttributeError at /ViewName/ 'module' object has no attribute 'SerializerMethodField'Postwar
It's a clashing of the names serializers.py at the your project, and serialisers at the rest_framework. Try this: from rest_framework import serializers as rest_serializers and location = rest_serializers.SerializerMethodField('get_alternate_name')Ankledeep
How would this workout with create and edit requests?Lifesaver
As far as I understand, it will not affect the creation or updating, because it's a read-only field (see the doc)Ankledeep
Exactly, the question never limits the requirement to get. He wants the field name to be changed for all type of requests.Lifesaver
the question never limits the requirement to get - Why do you think so? From what I see in the code there is an attempt to use a function result (def alias_alternate_name()) as a serializer field and nothing else.Ankledeep
Line no 13 of 'Zen of Python' : "There should be one -- and preferably only one --obvious way to do it."Lifesaver
This should not be the accepted answer. See the one below, that has almost 5 times as many upvotes at the time of me writing this.Dunbar
This is a bad solution. Use the source kwarg instead as described below.Ezana
class ParkSerializer(serializers.ModelSerializer): location = serializers.CharField(source='alternate_name', allow_null=True) class Meta: model = Park fields = ('other_fields', 'location')Parlormaid
R
23

This would work for write operations also

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.CharField(source='alternate_name')

    class Meta:
        model = Park
        fields = ('id', 'name', 'location')
Rossiter answered 19/4, 2017 at 10:5 Comment(0)
S
1

Another method

class UrlHyperlinkedModelSerializer(serializers.HyperlinkedModelSerializer):
    field_name_map = {}

    def to_representation(self, instance):
        res = super().to_representation(instance)
        nres = res.__class__()
        for k, v in res.items():
            nres[self.field_name_map.get(k, k)] = v
        return nres

class CommentSerializer(UrlHyperlinkedModelSerializer):
    field_name_map = {
        'a': 'a_url'
    }

    class Meta:
        model = models.Comment
        fields = ['a', 'url', 'body', 'created_at']

Sen answered 25/4, 2021 at 15:34 Comment(2)
Some explanations as to what is going on and why would be nice.Rolan
Did not work for me.Mitchell
B
-2

I know this is nearly 2 years later, but thought it might be of help for any future devs...

The reason the code doesn't work is because of the following line in models.py
location = serializers.Field(source='alias_alternate_name')
That's what the exception is trying to tell us. That is:
AttributeError at /ViewName/ 'module' object has no attribute 'Field'
means that the serializers module has no item called Field.

I want to offer a different solution than the other answers:
Modify models.py to include the related_name field and then simply use this name in serializers.

The advantage of this approach is that you have one on one mapping of field name in the serializer and field name in models with less code and the disadvantage of this is that this approach may not work with complex foreign key relationships.

Here is a demo of my approach:

models.py

class Park(models.Model):
    name = models.CharField(max_length=256)
    alternate_name = models.CharField(max_length=256, blank=True, related_name='location')
    objects = models.GeoManager()

    class Meta:
        db_table = u'p_park'

serializers.py

class ParkSerializer(serializers.ModelSerializer):

    class Meta:
        model = Park
        fields = ('id', 'name', 'location')

Read more about this here: https://www.django-rest-framework.org/api-guide/relations/

Bolger answered 3/12, 2020 at 4:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.