I am trying to use Django, DRF, ViewSet and Serializers to build a simple weblog. I have a main app and inside it:
main\models.py:
from django.db import models
from django.contrib.auth.models import User
class TimeStampMixin(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Category(TimeStampMixin):
name=models.CharField(max_length=255)
parent = models.ForeignKey("self", on_delete=models.CASCADE, null=True, blank=True)
def __str__(self):
return self.name
class PostStatus(models.Model):
status = models.CharField(max_length=50)
def __str__(self):
return self.status
def get_draft_status():
return PostStatus.objects.get_or_create(status='draft')[0].id
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
title = models.CharField(max_length=200)
description = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
status = models.ForeignKey(PostStatus, on_delete=models.CASCADE, default=get_draft_status)
def __str__(self):
return self.title
class RelatedPost(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='related_posts')
related_post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='related_to')
def __str__(self):
return f"{self.post.title} related to {self.related_post.title}"
main\serializers.py:
from rest_framework import serializers, status
from rest_framework.response import Response
from .models import Post, RelatedPost, PostStatus, Category
class PostStatusSerializer(serializers.ModelSerializer):
class Meta:
model = PostStatus
fields = '__all__'
class RelatedPostSerializer(serializers.ModelSerializer):
class Meta:
model = RelatedPost
fields = '__all__'
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
related_posts = serializers.SerializerMethodField()
category = serializers.SerializerMethodField()
class Meta:
model = Post
fields = '__all__'
def get_related_posts(self, obj):
related_posts = obj.related_posts.all()
return [{'id': rp.related_post.id, 'title': rp.related_post.title} for rp in related_posts]
def get_category(self, obj):
if obj.category:
return {'id': obj.category.id, 'name': obj.category.name}
else:
return {}
main\views.py:
from .models import PostStatus, Post, RelatedPost, Category
from .serializers import PostSerializer, RelatedPostSerializer, PostStatusSerializer, CategorySerializer
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
class PostStatusViewSet(viewsets.ModelViewSet):
queryset = PostStatus.objects.all()
serializer_class = PostStatusSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
class RelatedPostViewSet(viewsets.ModelViewSet):
queryset = RelatedPost.objects.all()
serializer_class = RelatedPostSerializer
main\urls.py:
from django.contrib import admin
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import PostStatusViewSet, PostViewSet, RelatedPostViewSet, CategoryViewSet
router = DefaultRouter()
router.register(r'category', CategoryViewSet, basename='category')
router.register(r'post-status', PostStatusViewSet, basename='post-status')
router.register(r'posts', PostViewSet, basename='post')
router.register(r'related-posts', RelatedPostViewSet, basename='related-post')
urlpatterns = [
#path('admin/', admin.site.urls),
path('', include(router.urls)),
]
I need to return category include it's id and it's name (GET /posts/1), so I added category = serializers.SerializerMethodField()
and
def get_category(self, obj):
if obj.category:
return {'id': obj.category.id, 'name': obj.category.name}
else:
return {}
into PostSerializer
and it is working correctly as you can see in this screenshot (section number 1):
before adding those parts, it was like following screenshot:
The problem is, when I am using category = serializers.SerializerMethodField()
category's dropdown list in HTML form will get disappeared (section number 2 in first image). How I can have something include section number 1 and number 4 at the same time to have customized category response and a dropdown of category in HTML form?
You can simply override the serializer serialization method and replace category value with the "correct" format.
class PostSerializer(serializers.ModelSerializer):
related_posts = serializers.SerializerMethodField()
class Meta:
...
def get_related_posts(self, obj):
...
def to_representation(self, instance):
new_cat_repr = {
'id': instance.category.id,
'name': instance.category.name
}
representation = super().to_representation(instance)
representation.pop("category")
representation['category'] = new_cat_repr
return representation
P.S. SerializerMethodField is read-only.