Welcome back to our Django REST Framework (DRF) series! In Part 1, we set up our Django project with DRF. Part 2 added CRUD functionality for tasks, and Part 3 secured it with token authentication. In Part 4, we’re personalizing our Task Manager API—ensuring each user only sees and manages their tasks. This step makes your API truly user-specific!
By the end, your API will link tasks to their creators and restrict access accordingly, all tested with Postman. Ready to jump in?
Let’s dive in!
Table of Contents
- Step 1: Update the Task Model
- Step 2: Modify Views for User Ownership
- Step 3: Test with Postman
- Conclusion
- What’s Next?
Step 1: Update the Task Model
To tie tasks to their creators, add a created_by
field to the Task model.
Update tasks/models.py
Add a foreign key to the User
model:
from django.db import models
from django.contrib.auth.models import User
class Task(models.Model):
title = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True)
completed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
Apply Migrations
Run these commands to update your database:
bash
python manage.py makemigrations tasks
python manage.py migrate
NOTE: If you have existing tasks, Django will ask for a default value for created_by
. You can:
- Set a default user ID (e.g., 1 for the first user).
- Or delete db.sqlite3 (after backing up) and rerun migrations.
What’s this?
- The
created_by
field links each task to its creator. -
on_delete=models.CASCADE
removes tasks if the user is deleted.
Step 2: Modify Views for User Ownership
We need to update both the views and the serializer to handle user ownership. The view will set created_by
automatically, and the serializer will ensure created_by
isn’t required in the request.
Update tasks/serializers.py
Make created_by read-only so it’s not required in the request:
from rest_framework import serializers
from .models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ['id', 'title', 'description', 'completed', 'created_at', 'created_by']
read_only_fields = ['id', 'created_at', 'created_by'] # Make created_by read-only
Update tasks/views.py
Modify the TaskListCreateView
and TaskDetailView
:
from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication
from .models import Task
from .serializers import TaskSerializer
class TaskListCreateView(generics.ListCreateAPIView):
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
def get_queryset(self):
# Only show tasks created by the authenticated user
return Task.objects.filter(created_by=self.request.user)
def perform_create(self, serializer):
# Automatically set the creator when a task is created
serializer.save(created_by=self.request.user)
class TaskDetailView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
def get_queryset(self):
# Only allow access to tasks created by the authenticated user
return Task.objects.filter(created_by=self.request.user)
What’s Changed?
- Added
authentication_classes = [TokenAuthentication]
to both views for clarity, matching the globalDEFAULT_AUTHENTICATION_CLASSES
set in Part 3. - Used
get_queryset()
to filter tasks byself.request.user
. - Added
perform_create()
to setcreated_by
on task creation. This ensures users only see and edit their tasks, building on the authentication from Part 3 of the series.
Step 3: Test with Postman
Start your server:
python manage.py runserver
1. Register Two Users
User 1:
Method: POST
URL: http://127.0.0.1:8000/api/register/
Body: raw > JSON:
{
"username": "user1",
"password": "pass123"
}
Send. Copy the token.
User 2:
Same URL, Body:
{
"username": "user2",
"password": "pass123"
}
Send. Copy the token
2. Log In
For user1: POST to http://127.0.0.1:8000/api/login/
, Body:
{
"username": "user1",
"password": "pass123"
}
Expect {"token": "token1"}.
For user2: Same, expect {"token": "token2"}.
3. Add Tasks
User 1:
Method: POST
URL: http://127.0.0.1:8000/api/tasks/
Headers: Authorization: Token 130194a73c000f377f782214619e8bb8f3d69412
Body: raw > JSON:
{
"title": "User1 Task",
"description": "Task for User1",
"completed": false
}
Send. Expect 201 Created.
User 2:
Same URL, Headers: Token token2, Body:
{
"title": "User2 Task",
"description": "Task for User2",
"completed": false
}
4. Access Tasks
With User 1’s Token:
- Method: GET
- URL: http://127.0.0.1:8000/api/tasks/
- Headers: Token token1
- Send. Expect 200 OK with [{"title": "User1 Task", ...}].
With User 2’s Token:
- Same URL, Headers: Token token2
- Send. Expect 200 OK with [{"title": "User2 Task", ...}].
5. Test Access to Other User’s Tasks
User 2 Tries to Access User 1’s Task:
Method: GET
- URL:
http://127.0.0.1:8000/api/tasks/1/
- Headers: Token token2
- Send. Expect 404 Not Found (because get_queryset filters out tasks not owned by User2).
Conclusion
🎉Your Task Manager API now personalizes tasks—users only see and manage their to-dos. By adding created_by and filtering in the views, we’ve made the API user-specific while keeping it secure with token authentication.
Summary Checklist
- ✅Added created_by to the Task model
- ✅Updated the serializer to make created_by read-only
- ✅Modified views to filter and assign tasks by user
- ✅Tested with Postman to confirm ownership
What’s Next?
In Part 5, we’ll optimize the API with filtering, pagination, and search—to enhance our API’s usability. Stay tuned!