Reputation: 6005
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?
Upvotes: 150
Views: 246183
Reputation: 4470
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.
Upvotes: 1
Reputation: 1554
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__"
Upvotes: 18
Reputation: 4770
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')
Upvotes: 5
Reputation: 2108
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
Upvotes: 182
Reputation: 23144
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).
Upvotes: 12
Reputation: 21730
this worked fine for me:
class ItemSerializer(serializers.ModelSerializer):
category_name = serializers.ReadOnlyField(source='category.name')
class Meta:
model = Item
fields = "__all__"
Upvotes: 31
Reputation: 1001
Another thing you can do is to:
Item
model that returns the category name and 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
Upvotes: 42
Reputation: 33921
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')
Upvotes: 122