Lesson 4
Custom GET Method in Django Views
Introduction

Welcome to the final lesson in this course. In this session, we will focus on defining custom methods for views in Django. Customizing views allows you to meet specific requirements for your API endpoints, improving the flexibility and functionality of your application.

Our objective in this lesson is to define a custom GET method for the TodoListCreate view. By the end of this lesson, you'll understand how to create a custom GET method, format the returned data, and test the custom method effectively.

Recap of the Setup

Before diving into the code, let's briefly recap what we've set up so far in prior lessons. We've created models, serializers, views, and URLs for a simple Todo application.

Here’s a code block that encapsulates our existing setup:

Python
1# models.py 2from django.db import models 3 4class Tag(models.Model): 5 name = models.CharField(max_length=255) 6 7class Todo(models.Model): 8 task = models.CharField(max_length=255) 9 completed = models.BooleanField(default=False) 10 priority = models.IntegerField() 11 assignee = models.CharField(max_length=255, blank=True, null=True) 12 group = models.CharField(max_length=255, blank=True, null=True) 13 tags = models.ManyToManyField(Tag, blank=True) 14 15# serializers.py 16from rest_framework import serializers 17from .models import Todo, Tag 18 19class TagSerializer(serializers.ModelSerializer): 20 class Meta: 21 model = Tag 22 fields = ['id', 'name'] 23 24class TodoSerializer(serializers.ModelSerializer): 25 tags = TagSerializer(many=True) 26 27 class Meta: 28 model = Todo 29 fields = '__all__' 30 31# views.py 32from rest_framework import generics 33from .models import Todo 34from .serializers import TodoSerializer 35 36class TodoListCreate(generics.ListCreateAPIView): 37 queryset = Todo.objects.all() 38 serializer_class = TodoSerializer 39 40class TodoDetail(generics.RetrieveAPIView): 41 queryset = Todo.objects.all() 42 serializer_class = TodoSerializer 43 44# urls.py 45from django.urls import path 46from .views import TodoListCreate, TodoDetail 47 48urlpatterns = [ 49 path('todos/', TodoListCreate.as_view(), name='todo_list_create'), 50 path('todos/<int:pk>/', TodoDetail.as_view(), name='todo_detail'), 51]

This code sets up our Todo and Tag models and connects them with corresponding serializers and views. Note that we omit the Group model, priority model, and TagGroup model for brevity in this lesson, but they would be completely compatible with the materials of this lesson.

Implementing the Custom GET Method

Imgaine that we want to implement a functionality of showing Todo items grouped by the tags attached to them. Users might want to use this functionality to navigate their collection of Todo items easier.

Let's go through the steps to customize the GET method within the TodoListCreate view to group Todo items by their tags.

Here’s the complete code block for the TodoListCreate view with the custom GET method:

Python
1# views.py 2from rest_framework import generics 3from rest_framework.response import Response 4from .models import Todo, Tag 5from .serializers import TodoSerializer 6 7class TodoListCreate(generics.ListCreateAPIView): 8 queryset = Todo.objects.all() 9 serializer_class = TodoSerializer 10 11 def get(self, request, *args, **kwargs): 12 tags = Tag.objects.all() 13 result = {} 14 for tag in tags: 15 todos = Todo.objects.filter(tags=tag) 16 serializer = TodoSerializer(todos, many=True) 17 result[tag.name] = serializer.data 18 return Response(result)

Step-by-Step Explanation:

  1. Retrieve All Tags: tags = Tag.objects.all()

    • This retrieves all Tag objects from the database.
  2. Initialize Result Dictionary: result = {}

    • We create an empty dictionary to store our results.
  3. Loop Through Tags:

    Python
    1for tag in tags: 2 todos = Todo.objects.filter(tags=tag) 3 serializer = TodoSerializer(todos, many=True) 4 result[tag.name] = serializer.data
    • For each tag, filter Todo items that are associated with that tag.
    • Serialize these Todo items.
    • Store the serialized data in the result dictionary, with the tag's name as the key.
  4. Return Response: return Response(result)

    • Finally, return the result dictionary as a JSON response.

This custom GET method groups Todo items by their tags and returns the grouped data in a structured format.

Returned Data Format

The custom GET method returns data formatted with tags as keys and associated Todo items as values. Here's what the response might look like:

JSON
1{ 2 "Urgent": [ 3 { 4 "id": 1, 5 "task": "Complex Task", 6 "completed": false, 7 "priority": 1, 8 "assignee": null, 9 "group": "Group A", 10 "tags": [ 11 {"id": 1, "name": "Urgent"}, 12 {"id": 2, "name": "Home"} 13 ] 14 } 15 ], 16 "Home": [ 17 { 18 "id": 1, 19 "task": "Complex Task", 20 "completed": false, 21 "priority": 1, 22 "assignee": null, 23 "group": "Group A", 24 "tags": [ 25 {"id": 1, "name": "Urgent"}, 26 {"id": 2, "name": "Home"} 27 ] 28 } 29 ] 30}
  • The response JSON object has tags as keys (e.g., "Urgent", "Home").
  • The value for each key is a list of Todo items associated with that tag.
Testing the Custom GET Method

Testing your custom view methods is crucial to ensure they work as expected. Let's write a test case for our custom GET method.

Python
1# tests.py 2from django.urls import reverse 3from rest_framework import status 4from rest_framework.test import APITestCase 5from .models import Todo, Tag 6 7class ManyToManySerializersViewsTestCase(APITestCase): 8 fixtures = ['many_to_many_serializers_views.json'] 9 10 def setUp(self): 11 self.list_url = reverse('todo_list_create') 12 13 def test_get_todos_grouped_by_tags(self): 14 response = self.client.get(self.list_url) 15 self.assertEqual(response.status_code, 200) 16 data = response.data 17 self.assertIn('Urgent', data) 18 self.assertIn('Home', data) 19 self.assertEqual(len(data['Urgent']), 1) 20 self.assertEqual(len(data['Home']), 1)
  • data = response.data: Retrieve the data from the response.
  • self.assertIn('Urgent', data) and self.assertIn('Home', data): Ensure the keys 'Urgent' and 'Home' are in the response data.
  • self.assertEqual(len(data['Urgent']), 1) and self.assertEqual(len(data['Home']), 1): Check the length of items under each key to ensure correctness.
Summary And Next Steps

In this final lesson, we explored how to define a custom GET method for a view in Django. We recapped our setup, delved into the details of view methods, implemented a custom GET method, examined the returned data format, and wrote test cases to ensure functionality.

By completing this lesson and the course, you have gained crucial skills in setting up and customizing API endpoints in Django. I encourage you to practice further with the exercises provided and explore additional customizations independently.

Congratulations on finishing the course, and keep building awesome Django applications!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.