modbender
modbender

Reputation: 419

Get First and Last Related Object for Django Rest API

I have Django Rest API serializers based on 2 models, Book and Chapter

class Book(models.Model):
    title = models.TextField(unique=True)
    description = models.TextField(blank=True, null=True)
    published_at = models.DateTimeField(default=timezone.now, db_index=True)
    added_at = models.DateTimeField(auto_now_add=True, db_index=True)
    modified_at = models.DateTimeField(auto_now=True, db_index=True)

class Chapter(models.Model):
    book = models.ForeignKey(
        Book, on_delete=models.CASCADE,
        related_name='chapters', db_index=True
    )
    published_at = models.DateTimeField(default=timezone.now, db_index=True)
    added_at = models.DateTimeField(auto_now_add=True, db_index=True)
    modified_at = models.DateTimeField(auto_now=True, db_index=True)

In the Book serializer I want 2 custom fields

Each having objects of model Chapter as you can understand from the field name first and last objects order by published_at field

The Chapter model has a Foreign Key to Book.

I tried something like below to get the last chapter

class LastChapterField(serializers.RelatedField):
    
    def get_queryset(self):
        return core_models.Chapter.objects\
            .filter(book=M.OuterRef("pk"))\
            .select_related("number")\
            .order_by("-published_at")[:1]

But I don't see the last chapter included in my results even though it is explicitly mentioned in fields of Book Serializer

I want the complete object in returning result and I want to be able to use order by on one of these nested fields (first_chapter or last_chapter) of Chapter model object.

Upvotes: 2

Views: 124

Answers (1)

ThomasGth
ThomasGth

Reputation: 870

You can do this by using a SerializerMethodField that will retrieve the objects you want and use an other serializer in it:

# serializers.py
from rest_framework import serializers


class ChapterSerializer(serializers.ModelSerializer):

    class Meta:
        model = Chapter
        fields = ('published_at', 'modified_at', 'added_at',)


class BookSerializer(serializers.ModelSerializer):
    first_chapter = serializers.SerializerMethodField()
    last_chapter = serializers.SerializerMethodField()

    def get_first_chapter(self, instance):
        return ChapterSerializer(instance.chapters.order_by('published_at').first()).data

    def get_last_chapter(self, instance):
        return ChapterSerializer(instance.chapters.order_by('published_at').last()).data

    class Meta:
        model = Book
        fields = ('first_chapter', 'last_chapter',
                  [...]  # Add the other fields you want to display here
                 )

Then you can use your serializer in your views, e.g.:

# views.py
from .serializers import BookSerializer
from .models import Book
from rest_framework.generics import RetrieveAPIView


class BookRetrieveView(RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

Upvotes: 1

Related Questions