Home > other >  Add a field to DRF generic list view
Add a field to DRF generic list view

Time:10-12

I need to create a DRF list view that shows each course along with a boolean field signifying whether the user requesting the view is subscribed to the course.

Course subscriptions are stored in the following model:

class Subscription(models.Model):
   user = models.ForeignKey(
      CustomUser, related_name='subscriptions', null=False, 
      on_delete=CASCADE)
   course = models.ForeignKey(
      Course, related_name='subscriptions', null=False,
      on_delete=CASCADE)

   class Meta:
      ordering = ['course', 'user']
      unique_together = [['user', 'course']]

This is the view I am writing:

class CourseListView(generics.ListAPIView):
   permission_classes = [IsAuthenticated, ]
   queryset = Course.objects.all()
   serializer_class = CourseSerializer
   
   def isSubscribed(self, request, course):
      sub = Subscription.objects.filter(
         user=request.user, course=course).first() 
      return True if sub else False

   def list(self, request, format=None):
      queryset = Course.objects.all()
      serializer = CourseSerializer(queryset, many=True)
      return Response(serializer.data)

I am looking for a way to modify the list method, so as to add to the response the information about whether request.user is subscribed to each of the courses.

The best solution I have for now is to construct serializer manually, which would look (at the level of pseudo-code) something like this:

serializer = []
for course in querySet:
    course['sub'] = self.isSubscribed(request, course)
    serializer.append(CourseSerializer(course))

I suspect there should be a better (standard, idiomatic, less convoluted) way for adding a custom field in a list view, but could not find it. In addition, I am wondering whether it is possible to avoid a database hit for every course.

CodePudding user response:

You can do that easily with Exists:

just change your queryset in your view:

from django.db.models import Exists, OuterRef

class CourseListView(generics.ListAPIView):
    permission_classes = [IsAuthenticated, ]
    serializer_class = CourseSerializer

    def get_queryset(self):
        subquery = Subscription.objects.filter(user=request.user, course=OuterRef('id'))
        return Course.objects.annotate(sub=Exists(subquery))

and add a field for it in your serializer:

class CourseSerializer(serializers.ModelSerializer):
    sub = serializers.BooleanField(read_only=True)

    class Meta:
        model = Course
        fields = '__all__'
  • Related