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?
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')
In the current DRF(3.6.3) version this worked for me
class ItemSerializer(serializers.ModelSerializer):
category_name = serializers.CharField(source='category.name')
class Meta:
model = Item
fields = ('id', 'name', 'category_name')
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
this worked fine for me:
class ItemSerializer(serializers.ModelSerializer):
category_name = serializers.ReadOnlyField(source='category.name')
class Meta:
model = Item
fields = "__all__"
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).
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__