How to get specific fields from child serializers? - django

Say, there are two models:
class Model1(models.Model):
r = models.CharField(max_length=200)
class Model2(models.Model):
p = models.CharField(max_length=200)
m = models.ForeignKey(Model1,on_delete=models.CASCADE)
The serializers are :
class Model1Serializer(serializers.Serializer):
class Meta:
model = Model1
fields = '__all__'
class Model2Serializer(serializers.Serializer):
class Meta:
model = Model2
fields = '__all__'
The given Model1 serializer returns the output as:
{
"id": 1,
"r": "r_value"
}
and model 2 serializer output is:
{
"id":1,
"p: "p_value",
"m": 1
}
The thing is that I also want the r value in the model2 serializer output. How to do that?

You need to specify new field with correct source - you can read more in docs.
Option 1: With class Serializer:
class Model2Serializer(serializers.Serializer):
id = serializers.IntegerField()
p = serializers.CharField()
r = serializers.CharField(source='m.r')
class Meta:
model = Model2
fields = '__all__'
Output: {'p': u'pppp', 'r': u'rrrrr', 'id': 1}
Option 2: With class ModelSerializer:
class Model2Serializer(serializers.ModelSerializer):
r = serializers.CharField(source='m.r')
class Meta:
model = Model2
fields = '__all__'
Output: {'p': u'pppp', 'r': u'rrrrr', 'm': 1L, u'id': 1}
Option 3: To include whole Model1:
class Model1Serializer(serializers.ModelSerializer):
class Meta:
model = Model1
fields = '__all__'
class Model2Serializer(serializers.Serializer):
m = Model1Serializer()
class Meta:
model = Model2
fields = '__all__'
Output: {'m': OrderedDict([(u'id', 1), ('r', u'rrrrr')])}

1. If you want r as attrs of m
define Serializer class of specific Relational Field
class Model2Serializer(serializers.ModelSerializer):
m = Model1Serializer()
class Meta:
model = Model2
fields = '__all__'
output:
[
{
"id": 1,
"m": {
"id": 1,
"r": "RED"
},
"p": "Light RED"
},
{
"id": 2,
"m": {
"id": 1,
"r": "RED"
},
"p": "Dark RED"
}
]
2. If you want just r Using ReadOnlyField
You can use ReadOnlyField
class Model2Serializer(serializers.ModelSerializer):
r = serializers.ReadOnlyField(source='m.r')
class Meta:
model = Model2
fields = '__all__'
output:
[
{
"id": 1,
"r": "RED",
"p": "Light RED",
"m": 1
},
{
"id": 2,
"r": "RED",
"p": "Dark RED",
"m": 1
}
]
3. If you want just r Using SerializerMethodField()
You can you SerializerMethodField and ist read only as well
class Model2Serializer(serializers.ModelSerializer):
r = serializers.SerializerMethodField()
class Meta:
model = Model2
fields = '__all__'
def get_r(self, instance):
return instance.m.r
output:
[
{
"id": 1,
"r": "RED",
"p": "Light RED",
"m": 1
},
{
"id": 2,
"r": "RED",
"p": "Dark RED",
"m": 1
}
]

Try this
class Model1Serializer(serializers.Serializer):
class Meta:
model = Model1
fields = '__all__'
class Model2Serializer(serializers.Serializer):
r = Model1Serializer(many=True, source="model1_set")
class Meta:
model = Model2
fields = '__all__'

DRF makes it quite easy if you already have a Model1Serializer for Model1:
class Model2Serializer(serializers.Serializer):
m = Model1Serializer()
class Meta:
model = Model2
fields = '__all__'
That should be it.

Simply use depth=1 in your serializer Meta. Like this:
class Model2Serializer(serializers.Serializer):
class Meta:
model = Model2
fields = '__all__'
depth = 1
This will go for a level of 1 into the Foreign keys that do exist in that model. (By the way, I don't recommend it for big models. But it's perfectly suitable for your situation)

Related

serializer only certain fields in foreing key relation

I'm trying to serializer two nested models linked by a foreing key:
class Category(models.Model):
sp = models.ForeignKey('species.Sp', on_delete=models.CASCADE, related_name='species_category')
category_name = models.CharField(max_length=50)
class Catch(models.Model):
weight = models.IntegerField()
category = models.ForeignKey('species.Category', on_delete=models.CASCADE,)
I know is possible to use depth option, but it serialize all the fields of the related model, for example:
class CatchesSerializer(serializers.ModelSerializer):
class Meta:
model = Catch
fields = ['id', 'weight', 'category', ]
depth = 1
returns
[
{
"id": 335,
"weight": 4710,
"category": {
"id": 1,
"category_name": "1",
"sp": 41
}
},
...
]
How is the way to serialize only certains fields of the related model? for example:
[
{
"id": 335,
"weight": 4710,
"category": {
"sp": 41,
}
},
...
]
Serializer can be nested, you can try:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['sp']
class CatchesSerializer(serializers.ModelSerializer):
category = CategorySerializer()
class Meta:
model = Catch
fields = ['id', 'weight', 'category']

Change Django-Rest-Framework serializer from flat to nested?

As per this example of nested and flat:
https://docs.python-guide.org/scenarios/serialization/
I want the foreign key to represented from the following JSON output
{
"cart": {
"cartid": "C0001",
"username": "myuser1"
},
"subtotal": 150.0,
"start_day": "2019-03-20T00:00:00"
},
to be represented like:
{
"C0001": {
"username": "myuser1"
},
"subtotal": 150.0,
"start_day": "2019-03-20T00:00:00"
},
Is there a simple way to have this output in django-rest-framework?
Here are my serializers:
class CartSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='shopper.username')
class Meta:
model = Cart
fields = ['shopper', 'cartid', 'username']
class CartProdSerializer(serializers.ModelSerializer):
cart = CartSerializer(read_only=True, label=Cart.cartid)
class Meta:
model = cart_prod
fields = ['cart', 'subtotal', 'start_day']
And my view:
class CartProdView(viewsets.ModelViewSet):
queryset = cart_prod.objects.all()
serializer_class = CartProdSerializer

Disable read only fields in the response after posting the data

My model:
class NewsID(models.Model):
pass
class News(models.Model):
newsId=models.ForeignKey(NewsID,related_name = 'News', on_delete=models.CASCADE)
lang_code=models.CharField(max_length=2)
headline=models.CharField(max_length=100)
class DefaultLanguage(models.Model):
news_id = models.ForeignKey(NewsID,related_name = 'default', on_delete=models.CASCADE)
defaultLanguage_id = models.IntegerField(unique=True)
My serializer:
class NewsSerializer(QueryFieldsMixin,serializers.ModelSerializer):
class Meta:
model=News
fields=('lang_code','headline')
class DefaultLanguageSerializer(QueryFieldsMixin,serializers.ModelSerializer):
class Meta:
model = DefaultLanguage
fields = ('news_id ','defaultLanguage_id ')
read_only_fields = ['news_id ','defaultLanguage_id ']
class NewsIDSerializer(QueryFieldsMixin,serializers.ModelSerializer):
News = NewsSerializer(many=True)
default_language = serializers.CharField(max_length=2,write_only=True)
class Meta:
model = NewsID
fields = ('id','News','default_language')
I want the response as below after posting :
{
"id": 10,
"News": [
{
"lang_code": "en",
"headline": "Breaking news"
}
],
"default_language": "en"
}
Now my response looks as below:
{
"id": 10,
"News": [
{
"lang_code": "en",
"headline": "Breaking news"
}
]
}
Now my response is having news_id and defaultLanguage_id (read only fields) I don't want the response with read only fields after posting. How to achieve this using Django?
change your serializer from
class NewsIDSerializer(QueryFieldsMixin,serializers.ModelSerializer):
News = NewsSerializer(many=True)
default = DefaultLanguageSerializer(many=True,read_only=True)
default_language = serializers.CharField(max_length=2,write_only=True)
class Meta:
model = NewsID
fields = ('id','News','default','default_language')
To
class NewsIDSerializer(QueryFieldsMixin,serializers.ModelSerializer):
News = NewsSerializer(many=True)
default_language = serializers.CharField(max_length=2)
class Meta:
model = NewsID
fields = ('id','News','default_language')
It will work as you expected.

Get StringRelatedField along with PrimaryKeyRelatedField using Serializer in DRF

Sorry for this newbie question, but I simply cannot find my way reading the manual.
models
#Subject
class TemaPai(models.Model):
subject = models.TextField()
disciplines = models.ManyToManyField(Materia)
# order = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.subject
class Meta:
verbose_name_plural = "temas-pais (subjects)"
# Order and junction
class TemaPaiOrdem(models.Model):
subject = models.ForeignKey(TemaPai, on_delete=models.CASCADE)
discipline = models.ForeignKey(Materia, on_delete=models.CASCADE)
order = models.IntegerField()
def __str__(self):
var2 = self.subject.subject
var = self.discipline.discipline
return var2 + ' - ' + var
class Meta:
verbose_name_plural = "Temas-pais-ordem"
unique_together = ('subject', 'discipline')
serializers
class TemaPaiSerializer(serializers.ModelSerializer):
disciplines = serializers.PrimaryKeyRelatedField(many=True, queryset=Materia.objects.all())
class Meta:
model = TemaPai
fields = ('id', 'subject', 'url', 'disciplines')
class TemaPaiOrdemSerializer(serializers.ModelSerializer):
discipline = serializers.PrimaryKeyRelatedField(queryset=Materia.objects.all())
subject = serializers.PrimaryKeyRelatedField(queryset=TemaPai.objects.all())
class Meta:
model = TemaPaiOrdem
fields = ('id', 'subject','discipline', 'order')
Well, TemaPaiOrdemSerializer is giving me a list like this:
[
{
"id": 1,
"subject": 1,
"discipline": 1,
"order": 1
},
{
"id": 2,
"subject": 2,
"discipline": 1,
"order": 11
}
]
It is fine. But I want to retrieve the subject string representation (from TemaPai model) as well, as a read_only field. So my desired list would be something like:
[
{
"id": 1,
"subject": 1,
"subject_name": "Introduction",
"discipline": 1,
"order": 1
},
{
"id": 2,
"subject": 2,
"subject_name": "Advanced stuff",
"discipline": 1,
"order": 11
}
]
I am trying to use
subject_name = serializers.ReadOnlyField(source:'subject')
with no success. Any hint would be appreciated.
Use ,
subject_name = serializers.StringRelatedField(source='subject',read_only=True)
hence your serializer will be like,
class TemaPaiOrdemSerializer(serializers.ModelSerializer):
discipline = serializers.PrimaryKeyRelatedField(queryset=Materia.objects.all())
subject = serializers.PrimaryKeyRelatedField(queryset=TemaPai.objects.all())
subject_name = serializers.StringRelatedField(source='subject',read_only=True)
class Meta:
model = TemaPaiOrdem
fields = ('id', 'subject', 'subject_name', 'discipline', 'order')
You can use . in source argument for lookup related model's field like this:
subject_name = serializers.ReadOnlyField(source='subject.subject')

django rest framework return a custom object using ModelSerializer and ModelViewSet

I have three models, three serializers, one modelviewset below.
I am using django-rest-framework to make a rest api for android.
The restaurant model was created first. Then I created a star model and an image model.
What I want to do is to add star and image objects into restaurant objects.
finally I've got what I want result but I think my viewset code looks like wrong..
Is there another way not to use "for loop"?
Models
class Restaurant(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
address = models.CharField(max_length=255)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
weather = models.ForeignKey(Weather, on_delete=models.CASCADE)
distance = models.ForeignKey(Distance, on_delete=models.CASCADE)
description = models.TextField('DESCRIPTION')
def __str__(self):
return self.name
class Star(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.IntegerField('RATING')
def __str__(self):
return self.restaurant
class RestaurantImage(models.Model):
id = models.AutoField(primary_key=True)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
path = models.CharField(max_length=255)
Serializer
class StarSerializer(serializers.ModelSerializer):
class Meta:
model = Star
fields = ('id', 'restaurant', 'user', 'rating', )
class RestaurantDetailSerializer(serializers.ModelSerializer):
category = CategorySerializer()
weather = WeatherSerializer()
distance = DistanceSerializer()
class Meta:
model = Restaurant
fields = ('id', 'name', 'address', 'category', 'weather',
'distance', 'description', )
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantImage
fields = ('id', 'path', 'restaurant')
ViewSet
class RestaurantDetailInfoViewSet(viewsets.ModelViewSet):
queryset = Restaurant.objects.all()
serializer_class = RestaurantSerializer
def list(self, request, *args, **kwargs):
restaurant_list = Restaurant.objects.all()
restaurant_result = []
for restaurant in restaurant_list:
restaurantInfo = Restaurant.objects.filter(id=restaurant.pk)
restaurant_serializer = RestaurantDetailSerializer(restaurantInfo, many=True)
ratingAverageValue = Star.objects.filter(restaurant=restaurant.pk).aggregate(Avg('rating'))
images = RestaurantImage.objects.filter(restaurant=restaurant.pk)
image_serializer = ImageSerializer(images, many=True)
restaurant_dic = {
'restaurant': restaurant_serializer.data,
'ratingAverage': ratingAverageValue['rating__avg']
if ratingAverageValue['rating__avg'] is not None else 0,
'images': image_serializer.data
}
restaurant_result.append(restaurant_dic)
return Response(restaurant_result)
Result
[
{
"restaurant": [
{
"id": 1,
"name": "restaurant1",
"address": "address1",
"category": {
"c_id": 1,
"name": "foodtype1"
},
"weather": {
"w_id": 1,
"name": "sunny"
},
"distance": {
"d_id": 1,
"name": "inside"
},
"description": "description1"
}
],
"ratingAverage": 2.6667,
"images": [
{
"id": 1,
"path": "imagepath",
"restaurant": 1
}
]
},
Solution:
class RestaurantDetailSerializer(serializers.ModelSerializer):
category = CategorySerializer()
weather = WeatherSerializer()
distance = DistanceSerializer()
images = ImageSerializer(many=True, read_only=True)
ratingAverage = serializers.SerializerMethodField(read_only=True)
def get_ratingAverage(self, restaurant):
ratingAvgVal = Star.objects.filter(
restaurant=restaurant
).aggregate(Avg('rating'))['rating__avg']
return ratingAvgVal if ratingAvgVal is not None else 0
class Meta:
model = Restaurant
fields = ('id', 'name', 'address', 'category', 'weather',
'distance', 'description', 'images', 'ratingAverage', )
Explanation:
Here, I have nested the ImageSerializer in the RestaurantSerializer class, since you needed all the fields you've defined in ImageSerializer.
Then, for ratingAverage, I have used the SerializerMethodField which returns the value calculated (your logic) in the method I've defined for it, i.e. get_ratingAverage, which takes the Restaurant instance reference passed as an argument to the method for the field.