145

I'm using the django rest framework to create an API. I have the following models:

class Category(models.Model):
    name = models.CharField(max_length=100)

    def __unicode__(self):
        return self.name


class Item(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category, related_name='items')

    def __unicode__(self):
        return self.name

To create a serializer for the categories I'd do:

class CategorySerializer(serializers.ModelSerializer):
    items = serializers.RelatedField(many=True)

    class Meta:
        model = Category

... and this would provide me with:

[{'items': [u'Item 1', u'Item 2', u'Item 3'], u'id': 1, 'name': u'Cat 1'},
 {'items': [u'Item 4', u'Item 5', u'Item 6'], u'id': 2, 'name': u'Cat 2'},
 {'items': [u'Item 7', u'Item 8', u'Item 9'], u'id': 3, 'name': u'Cat 3'}]

How would I go about getting the reverse from an Item serializer, ie:

[{u'id': 1, 'name': 'Item 1', 'category_name': u'Cat 1'},
{u'id': 2, 'name': 'Item 2', 'category_name': u'Cat 1'},
{u'id': 3, 'name': 'Item 3', 'category_name': u'Cat 1'},
{u'id': 4, 'name': 'Item 4', 'category_name': u'Cat 2'},
{u'id': 5, 'name': 'Item 5', 'category_name': u'Cat 2'},
{u'id': 6, 'name': 'Item 6', 'category_name': u'Cat 2'},
{u'id': 7, 'name': 'Item 7', 'category_name': u'Cat 3'},
{u'id': 8, 'name': 'Item 8', 'category_name': u'Cat 3'},
{u'id': 9, 'name': 'Item 9', 'category_name': u'Cat 3'}]

I've read through the docs on reverse relationships for the rest framework but that appears to be the same result as the non-reverse fields. Am I missing something obvious?

Community
  • 1
  • 1
hellsgate
  • 5,905
  • 5
  • 32
  • 47

8 Answers8

175

In the DRF version 3.6.3 this worked for me

class ItemSerializer(serializers.ModelSerializer):
    category_name = serializers.CharField(source='category.name')

    class Meta:
        model = Item
        fields = ('id', 'name', 'category_name')

More info can be found here: Serializer Fields core arguments

mpliax
  • 49
  • 1
  • 8
Sayok88
  • 2,038
  • 1
  • 14
  • 22
  • 4
    But you have to be ware cause it should threw NoneType error if the category field In the Item model is set to blank=True – Desert Camel Oct 28 '20 at 04:55
  • 4
    @DesertCamel You can simply add `allow_null=True` to your serializer CharField to overcome this – MohitC Dec 24 '21 at 17:02
121

Just use a related field without setting many=True.

Note that also because you want the output named category_name, but the actual field is category, you need to use the source argument on the serializer field.

The following should give you the output you need...

class ItemSerializer(serializers.ModelSerializer):
    category_name = serializers.RelatedField(source='category', read_only=True)

    class Meta:
        model = Item
        fields = ('id', 'name', 'category_name')
Tom Christie
  • 33,394
  • 7
  • 101
  • 86
  • 27
    what about retriving all the fields of catagory model?? – A.J. Feb 19 '14 at 11:55
  • 17
    if you want to retrieve all the fields of category model make the category serializer and have it in code like category_name = CategorySerliazer() – Faizan Ali Dec 01 '14 at 11:31
  • 9
    I tried to do this but i'm getting a error `Relational field must provide a 'queryset' argument, or set read_only='True' ` – ePascoal Jul 23 '15 at 09:03
  • or providing a queryset attribute if you want to support create/update, something like: `category_name = serializers.RelatedField(source='category', queryset=Category.objects.all())` I suppose. – stelios Oct 13 '16 at 18:17
  • in my case `categories= CategorySerializer(source='category', read_only=True)` it works django 2.07 – JackNavaRow Aug 22 '18 at 18:32
  • the serializer approach seems to work, but based on the logs, it seems to do an additional select for each relation it serializes (e.g. for each category name). any way to accomplish this with just a single db call? – Farhad Abdolhosseini Mar 04 '19 at 04:48
  • 1
    If you get `AssertionError:...` use this answer https://stackoverflow.com/a/44530606/5403449 – Josh Apr 29 '19 at 13:00
  • @Tom Christie In the question, the OP is looking for a `CategorySerializer` but the answers are showing an `ItemSerializer`. Why the difference here? – DjangoBlockchain Feb 18 '20 at 15:36
  • Instead of RelatedField, you should use ReadOnlyField, it still has the source parameter :) – Greko2015 GuFn Dec 04 '20 at 10:36
  • @DjangoBlockchain that's not the case, my question is about creating the item serializer – hellsgate Apr 07 '21 at 13:19
  • But if category in Item is manyTomany field, then how we will render it in this serialization? – Muhammad Abubakar Zorrain May 23 '22 at 07:56
42

Another thing you can do is to:

  • create a property in your Item model that returns the category name and
  • expose it as a ReadOnlyField.

Your model would look like this.

class Item(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category, related_name='items')

    def __unicode__(self):
        return self.name

    @property
    def category_name(self):
        return self.category.name

Your serializer would look like this. Note that the serializer will automatically get the value of the category_name model property by naming the field with the same name.

class ItemSerializer(serializers.ModelSerializer):
    category_name = serializers.ReadOnlyField()

    class Meta:
        model = Item
hsebastian
  • 991
  • 1
  • 8
  • 8
30

this worked fine for me:

class ItemSerializer(serializers.ModelSerializer):
    category_name = serializers.ReadOnlyField(source='category.name')
    class Meta:
        model = Item
        fields = "__all__"
suhailvs
  • 20,182
  • 14
  • 100
  • 98
18

Simple solution source='category.name' where category is foreign key and .name it's attribute.

from rest_framework.serializers import ModelSerializer, ReadOnlyField
from my_app.models import Item

class ItemSerializer(ModelSerializer):
    category_name = ReadOnlyField(source='category.name')

    class Meta:
        model = Item
        fields = "__all__"
fedorqui
  • 275,237
  • 103
  • 548
  • 598
Anurag Misra
  • 1,516
  • 18
  • 24
12

Worked on 08/08/2018 and on DRF version 3.8.2:

class ItemSerializer(serializers.ModelSerializer):
    category_name = serializers.ReadOnlyField(source='category.name')

    class Meta:
        model = Item
        read_only_fields = ('id', 'category_name')
        fields = ('id', 'category_name', 'name',)

Using the Meta read_only_fields we can declare exactly which fields should be read_only. Then we need to declare the foreign field on the Meta fields (better be explicit as the mantra goes: zen of python).

John Moutafis
  • 22,254
  • 11
  • 68
  • 112
5

This solution is better because of no need to define the source model. But the name of the serializer field should be the same as the foreign key field name

class ItemSerializer(serializers.ModelSerializer):
    category = serializers.SlugRelatedField(read_only=True, slug_field='title')

    class Meta:
        model = Item
        fields = ('id', 'name', 'category')
zshanabek
  • 4,270
  • 3
  • 19
  • 27
0

For those that want to replace the field of the ForeignKey (that displays the ID) you can still used the __all__ syntax for convenience and simply over-write the field name as you see fit. For example:

class MyModelSerializer(serializers.ModelSerializer):

    # override the category field that would otherwise show an integer value 
    # for the ID with the field of that model you choose. "name" here.
    category = serializers.ReadOnlyField(source='category.name')

    class Meta:
        model = MyModel
        fields = '__all__'

IMO this is convenient b/c you can still use the __all__ syntax to capture any added fields later. Anything that's getting overridden is manually done so and, if needing to be reverted, can be manually done some without changing any other syntax.

alphazwest
  • 3,483
  • 1
  • 27
  • 39