DJANGO RF - Include the SerializerMethodField when nesting a serializer in another serializer - django

I have a serializer on my UserProfile model. It includes a SerializerMethodField "userGlobalPoints" :
class UserProfileSerializer(serializers.ModelSerializer):
userGlobalPoints = serializers.SerializerMethodField()
def get_userGlobalPoints(self, obj):
(*** calculation ***)
return userGlobalPoints
class Meta:
model = UserProfile
fields = '__all__'
In my Post serializer, I want to nest, for each post, information about the post's author. So I include fields from the UserProfile model (there is a ForeignKey relation in the Post model).
I would like to add the userGlobalPoints field. But I can't figure out how I can nest this field in the serializer. It looks like the usual syntax for nesting a serializer does not work on SerializerMethodField fields.
This is my PostSerializer:
class PostSerializer(serializers.ModelSerializer):
pk = serializers.ReadOnlyField()
authorNickname = serializers.CharField(source='postAuthor.userNickname', read_only=True)
authorProfileWithGlobalPoints = UserProfileSerializer(many=False, read_only=True)
class Meta:
model = Post
fields = ['pk','authorNickname','postText', 'postTimestamp', 'authorProfileWithGlobalPoints']
Any help on how to achieve this would be appreciated!
EDIT
Following a clever comment, the issue is not related to the SerializerMethodField. None of the fields of the nested serializer is serialized! I probably made a mistake in nesting the serializer in the reverse relation direction.
So the question does not need any more answers. Thank you all.
EDIT 2
The error was not caused by the MethodField. The issue is that I have nested serializers in the reverse direction of their relation. I tried to nest the author in the Post object, I should rather have nested the Post in the Author object. Since then I have changed my logic and it works.
Maybe an administrator could delete this topic, since it does not bring very much value to anybody.

What kind of error are you getting exactly?
Could it be that authorProfileWithGlobalPoints is not defined on the Post model? Maybe this solves it, by telling Django what to look for in the model:
class PostSerializer(serializers.ModelSerializer):
pk = serializers.ReadOnlyField()
authorProfileWithGlobalPoints = UserProfileSerializer(source='postAuthor', many=False, read_only=True)
class Meta:
model = Post
fields = ['pk','postText', 'postTimestamp', 'authorProfileWithGlobalPoints']

Related

Conditionally nest Django serializers

So, I have a foreign key to my User model in many of my models. Now, the serializers for these models are nested, in that they include the entire user object rather than just the id. I have done so as shown bellow:
class BadgeSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Badge
fields = '__all__'
It works as expected. However, I seldom find myself in a situation where I want just the id. I was wondering what is the best way to conditionally nest my BadgeSerializer...
Now, the best solution I can think of is to have a non-nested BadgeSerializer, which includes only the user id. And then have a NestedBadgeSerializer (extending BadgeSerializer) which does nest the User model and include the entire user object.
class BadgeSerializer(serializers.ModelSerializer):
class Meta:
model = Badge
fields = '__all__'
class NestedBadgeSerializer(BadgeSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Badge
fields = '__all__'
I am NOT sure if that's the proper way though.

Expected a dictionary, but got str Django Rest Framework

Right now I am creating a user department with a list of users that are a foreign key back to the main user model. I had this working yesterday, but for some reason I screwed it up. I imagine it has something to do with the serializers. I want to be able to post a list of users in this format
['jack', 'tom']
However, even using the raw data api this is not allowing me to do this. Here is my code:
Serializers:
class DepartmentSerializer(serializers.ModelSerializer):
user_department = UserSerializer(many=True)
class Meta:
model = Departments
fields = '__all__'
class DepartmentUpdateSerializer(serializers.ModelSerializer):
user_department = UserSerializer(many=True)
class Meta:
model = Departments
fields = ['department_name', 'department_head', 'user_department']
I swear yesterday it was allowing me to select from a list of users in the api. I could also post and it would work from the front end. However, now whenever I create a department it's expecting a dictionary, which I am not trying to pass.
Dudes, for whatever reason, removing () after the UserSerializer fixed it. If anyone can explain why that would be even better!
class DepartmentSerializer(serializers.ModelSerializer):
user_department = UserSerializer
class Meta:
model = Departments
fields =['department_name', 'department_head', 'user_department']
class DepartmentUpdateSerializer(serializers.ModelSerializer):
user_department = UserSerializer
class Meta:
model = Departments
fields = ['department_name', 'department_head', 'user_department']
When you use the nested serializer you need to add the nested serializer field (user_department in your case) to the fields too, as you can see you used
fields = '__all__'
which does not include your nested serializer field, you need to manually add that to the meta fields

Why did my Serializer stop recognizing a given required value?

I have the following Model and Serializer:
Model
class Location(models.Model):
company = models.ForeignKey(Company)
title = models.CharField(max_length=100)
class Meta:
ordering = ['company']
unique_together = ['company', 'title']
Serializer
class LocationSerializer(serializers.ModelSerializer):
company = serializers.StringRelatedField()
class Meta:
model = Location
When I try to create a new Location using the Serializer:
lo = LocationSerializer(data={'title': 'test', 'company': 2})
I get back the following error:
{'company': ['This field is required.']}
What gives? The only thing I'd changed recently in either the Model or the Serializer was adding the unique_together constraint to the Model. Why is the Serializer now unable to recognize the company value?
It turns out the error message is a complete red herring. What's really happening is that there's an incompatibility between the unique_together constraint in the Model and read-only fields like StringRelatedField defined in the Serializer.
Removing unique_together from the Model restored the Serializer's functionality. Alternatively, you could remove the read-only field declaration from the Serializer. Neither really seem like an appropriate solution as they require sacrificing functionality in pretty major ways.
Right now there appears to be an issue open on DRF's GitHub related to this problem but it remains to be seen if this is Working As Intendedtm or is in fact a bug.

How to order a Django Rest Framework ManyToMany related field?

I have a Django Rest Framework application with the following (simplified) models.py:
class Photo(models.Model):
...
class Album(models.Model):
...
photos = models.ManyToManyField(Photo, through='PhotoInAlbum', related_name='albums')
class PhotoInAlbum(models.Model):
photo = models.ForeignKey(Photo)
album = models.ForeignKey(Album)
order = models.IntegerField()
class Meta:
ordering = ['album', 'order']
And in my serializers.py, I have the following:
class AlbumSerializer(serializers.ModelSerializer):
...
photos = serializers.PrimaryKeyRelatedField('photos', many=True)
My question is, how can I have AlbumSerializer return the photos ordered by the field order?
The best solution to customise the queryset is using serializers.SerializerMethodField, but what shezi's reply is not exactly right. You need to return serializer.data from SerializerMethodField. So the solution should be like this:
class PhotoInAlbumSerializer(serialisers.ModelSerializer):
class Meta:
model = PhotoInAlbum
class AlbumSerializer(serializers.ModelSerializer):
# ...
photos = serializers.SerializerMethodField('get_photos_list')
def get_photos_list(self, instance):
photos = PhotoInAlbum.objects\
.filter(album_id=instance.id)\
.order_by('order')\
.values_list('photo_id', flat=True)
return PhotoInAlbumSerializer(photos, many=True, context=self.context).data
It looks as if the RelatedManager that handles the relationship for ManyToManyFields does not respect ordering on the through model.
Since you cannot easily add an ordering parameter to the serializer field, the easiest way to achieve ordering is by using a serializer method:
class AlbumSerializer(serializers.modelSerializer):
# ...
photos = serializers.SerializerMethodField('get_photos_list')
def get_photos_list(self, instance):
return PhotoInAlbum.objects\
.filter(album_id=instance.id)\
.order_by('order')\
.values_list('photo_id', flat=True)
Generally, the easiest way is to do this in your AlbumView or AlbumViewSet.
You can do this by filtering - in this case you should define a get_queryset method in your AlbumViewSet.
Anyway, this is a good solution as long as you only GET the data. If you want to have POST and PUT methods working with ordering the photos, you can do it in two ways:
stay with ManyToMany relation - patch the create method in AlbumViewSet and __create_items and restore_object method in AlbumSerializer
or
replace it with something more sophisticated - use django-sortedm2m field.
Note that the second solution does not mess with AlbumViewSet (and even AlbumSerializer!) - ordering logic stays in the relation field code.

django rest nested relation in post/put

I am new in django rest api developement. I have two models one is category and another is subcategories.
Here is my models
class Category(models.Model):
title = models.Charfield()
brief = models.TextField()
subcategories = model.ManyToManyField('Subcategory', blank=True)
My serializer class
class CategorySerializer(serializers.ModelSerializer):
title= serializer.Charfield()
subcategories = Relatedfield(many=True)
Now in view
def post(self, request, format = None):
data=request.DATA
serialize= CategorySerializer(data=request.DATA)
if serializer.valid():
serializer.save()
How to save nested data like {'title':"test",'subscategories':[{'description':'bla bla bla'},{'description':'test test'}]} in post method.
I have read this in documentation
Note: Nested serializers are only suitable for read-only
representations, as there are cases where they would have ambiguous or
non-obvious behavior if used when updating instances. For read-write
representations you should always use a flat representation, by using
one of the RelatedField subclasses.
Please let me suggest which is right way or solution to do nested relation post/put in django rest.
Have you tried creating a SubCategorySerializer and adding this as a field on CategorySerializer?
class SubcategorySerializer(serializers.ModelSerializer):
class Meta:
model = Subcategory
class CategorySerializer(serializers.ModelSerializer):
subcategories = SubcategorySerializer(many=True)
Docs: http://django-rest-framework.org/api-guide/relations.html#nested-relationships