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.
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:
Python1# 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.
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:
Python1# 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:
-
Retrieve All Tags:
tags = Tag.objects.all()
- This retrieves all
Tag
objects from the database.
- This retrieves all
-
Initialize Result Dictionary:
result = {}
- We create an empty dictionary to store our results.
-
Loop Through Tags:
Python1for 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.
- For each tag, filter
-
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.
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:
JSON1{ 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 your custom view methods is crucial to ensure they work as expected. Let's write a test case for our custom GET
method.
Python1# 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)
andself.assertIn('Home', data)
: Ensure the keys 'Urgent' and 'Home' are in the response data.self.assertEqual(len(data['Urgent']), 1)
andself.assertEqual(len(data['Home']), 1)
: Check the length of items under each key to ensure correctness.
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!