Tasks Views - REST API Endpoints
File: api/nextango/apps/tasks/views.py
Purpose: DRF ViewSets for task CRUD operations
Base URL: /api/tasks/
Framework: Django REST Framework (ModelViewSet)
Overview
Tasks views provide RESTful API endpoints for task management using DRF's ModelViewSet pattern. The TaskViewSet overrides the retrieve() method to use TaskService for enhanced task details with computed fields.
Architecture:
HTTP Request
│
├─ URL Routing (urls.py)
│
▼
TaskViewSet
│
├─ Standard CRUD (list, create, update, delete)
├─ Custom retrieve → TaskService.get_task_with_details()
│
▼
JSON Response
TaskViewSet
Purpose: Full CRUD operations for Task model with service-enhanced retrieval
Source Code:
# From views.py:9-27
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
lookup_field = 'task_id'
def retrieve(self, request, *args, **kwargs):
"""Override retrieve method to use TaskService for enhanced task data."""
# Get the task_id from the URL
task_id = kwargs.get('task_id')
try:
# Get enhanced task details from service
task_details = TaskService.get_task_with_details(task_id)
return Response(task_details, status=status.HTTP_200_OK)
except Task.DoesNotExist:
return Response(
{'error': 'Task not found'},
status=status.HTTP_404_NOT_FOUND
)
Configuration
Queryset:
queryset = Task.objects.all()
Performance Note: Should use select_related() to avoid N+1 queries for list endpoint:
# Recommended
queryset = Task.objects.select_related(
'assigned_to', 'completed_by', 'store'
).all()
Serializer:
serializer_class = TaskSerializer
Lookup Field:
lookup_field = 'task_id'
- Uses
task_idinstead of defaultpk(UUID) - URL pattern:
/api/tasks/{task_id}/instead of/api/tasks/{uuid}/ - Business-friendly identifiers in URLs
API Endpoints
List Tasks
Endpoint: GET /api/tasks/
Purpose: Retrieve all tasks (paginated)
Authentication: Required
Query Parameters:
- Standard DRF filtering/pagination parameters
page- Page number (if pagination enabled)page_size- Items per page
Response:
HTTP 200 OK
[
{
"id": "uuid-1",
"task_id": "task_12345",
"title": "Restock dairy section",
"description": "Restock milk, yogurt, and cheese products",
"assigned_to": "user-uuid-1",
"assigned_to_name": "John Doe",
"store": "store-uuid-1",
"store_name": "Downtown Location",
"priority": "high",
"status": "in_progress",
"due_date": "2025-11-20",
"completed_by": null,
"completed_by_name": null,
"completed_at": null,
"created_at": "2025-11-18T10:30:00Z"
},
...
]
Uses: TaskSerializer (standard ModelViewSet behavior)
Retrieve Task
Endpoint: GET /api/tasks/{task_id}/
Purpose: Get detailed task information with computed fields
Authentication: Required
Path Parameters:
task_id(string) - Business identifier (e.g., "task_12345")
Response:
HTTP 200 OK
{
"id": "uuid-1",
"task_id": "task_12345",
"title": "Restock dairy section",
"description": "Restock milk, yogurt, and cheese products",
"priority": "high",
"status": "in_progress",
"due_date": "2025-11-20",
"completed_at": null,
"created_at": "2025-11-18T10:30:00Z",
"assigned_to": {
"id": "user-uuid-1",
"name": "John Doe",
"employee_id": "EMP-001"
},
"completed_by": null,
"store": {
"id": "store-uuid-1",
"name": "Downtown Location",
"store_code": "DT-01"
},
"is_overdue": false,
"days_since_created": 0
}
Error Response (Not Found):
HTTP 404 Not Found
{
"error": "Task not found"
}
Uses: TaskService.get_task_with_details() (custom override)
Key Differences from List:
- Nested objects for relationships (assigned_to, completed_by, store)
- Computed fields (is_overdue, days_since_created)
- Enhanced data structure optimized for detail views
Create Task
Endpoint: POST /api/tasks/
Purpose: Create a new task
Authentication: Required
Request Body:
{
"title": "Clean refrigerators",
"description": "Deep clean all refrigerators in dairy section",
"assigned_to": "user-uuid-1",
"store": "store-uuid-1",
"priority": "medium",
"status": "pending",
"due_date": "2025-11-21"
}
Required Fields:
titlestorepriority
Optional Fields:
descriptionassigned_tostatus(defaults to 'pending')due_date
Response:
HTTP 201 Created
{
"id": "new-task-uuid",
"task_id": "task_67890",
"title": "Clean refrigerators",
"description": "Deep clean all refrigerators in dairy section",
"assigned_to": "user-uuid-1",
"assigned_to_name": "John Doe",
"store": "store-uuid-1",
"store_name": "Downtown Location",
"priority": "medium",
"status": "pending",
"due_date": "2025-11-21",
"completed_by": null,
"completed_by_name": null,
"completed_at": null,
"created_at": "2025-11-18T14:00:00Z"
}
Validation Errors:
HTTP 400 Bad Request
{
"title": ["This field is required."],
"priority": ["\"urgent\" is not a valid choice."]
}
Uses: TaskSerializer (standard ModelViewSet behavior)
Update Task (Full)
Endpoint: PUT /api/tasks/{task_id}/
Purpose: Replace entire task (all fields required)
Authentication: Required
Request Body:
{
"title": "Restock dairy section",
"description": "Updated description",
"assigned_to": "user-uuid-2",
"store": "store-uuid-1",
"priority": "high",
"status": "in_progress",
"due_date": "2025-11-20"
}
Response:
HTTP 200 OK
{
"id": "uuid-1",
"task_id": "task_12345",
"title": "Restock dairy section",
...
}
Uses: TaskSerializer (standard ModelViewSet behavior)
Update Task (Partial)
Endpoint: PATCH /api/tasks/{task_id}/
Purpose: Update specific fields only
Authentication: Required
Common Use Cases:
1. Update Status:
PATCH /api/tasks/task_12345/
{
"status": "in_progress"
}
2. Complete Task:
PATCH /api/tasks/task_12345/
{
"status": "completed",
"completed_by": "user-uuid-2"
}
3. Reassign Task:
PATCH /api/tasks/task_12345/
{
"assigned_to": "user-uuid-3"
}
Response:
HTTP 200 OK
{
"id": "uuid-1",
"task_id": "task_12345",
"status": "in_progress",
...
}
Uses: TaskSerializer with partial=True (standard ModelViewSet behavior)
Delete Task
Endpoint: DELETE /api/tasks/{task_id}/
Purpose: Delete a task
Authentication: Required
Response:
HTTP 204 No Content
Error Response (Not Found):
HTTP 404 Not Found
{
"detail": "Not found."
}
Uses: Standard ModelViewSet destroy() method
Custom retrieve() Implementation
Why Override retrieve()?
Standard Behavior:
# Default ModelViewSet.retrieve()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
Returns: Flat serializer data with UUID foreign keys
Custom Behavior:
# TaskViewSet.retrieve()
def retrieve(self, request, *args, **kwargs):
task_id = kwargs.get('task_id')
task_details = TaskService.get_task_with_details(task_id)
return Response(task_details)
Returns: Enhanced data with:
- Nested relationship objects
- Computed fields (is_overdue, days_since_created)
- Optimized queries (select_related)
Benefits:
- Richer Data: Detail view has more information than list view
- Computed Fields: Business logic (overdue status, age) included
- Performance: Service handles query optimization
- Consistency: Service logic reusable across different interfaces
URL Routing
Configuration:
# From urls.py:4-6
router = DefaultRouter()
router.register(r'tasks', TaskViewSet, basename='task')
# From urls.py:10-12
app_name = 'tasks'
urlpatterns = [
path('', include(router.urls)),
]
Generated URLs:
GET /api/tasks/ → TaskViewSet.list()
POST /api/tasks/ → TaskViewSet.create()
GET /api/tasks/{task_id}/ → TaskViewSet.retrieve() (custom)
PUT /api/tasks/{task_id}/ → TaskViewSet.update()
PATCH /api/tasks/{task_id}/ → TaskViewSet.partial_update()
DELETE /api/tasks/{task_id}/ → TaskViewSet.destroy()
Note: Uses task_id in URLs (not id or pk) due to lookup_field = 'task_id'
DeliveryScheduleViewSet (Commented Out)
Source Code:
# From views.py:29-32 (commented out)
# class DeliveryScheduleViewSet(viewsets.ModelViewSet):
# queryset = DeliverySchedule.objects.all()
# serializer_class = DeliveryScheduleSerializer
# lookup_field = 'delivery_id'
Status:
- ViewSet implementation commented out
- Model exists in models.py
- Serializer commented out in serializers.py
- URL routing commented out in urls.py
If Activated:
- Would provide CRUD for DeliverySchedule model
- URL pattern:
/api/delivery-schedules/{delivery_id}/ - Standard ModelViewSet behavior (no custom retrieve)
Authentication & Permissions
Current Configuration:
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
# No authentication_classes specified
# No permission_classes specified
Behavior:
- Uses project-wide default authentication (likely JWT or session)
- Uses project-wide default permissions (likely IsAuthenticated)
Recommended Explicit Configuration:
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.select_related('assigned_to', 'completed_by', 'store').all()
serializer_class = TaskSerializer
lookup_field = 'task_id'
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
# ... custom retrieve()
Performance Optimization
Current Issue: N+1 Queries on List
Problem:
queryset = Task.objects.all()
When listing tasks:
- Query 1:
SELECT * FROM tasks_task - Query 2-N:
SELECT name FROM users_user WHERE id = task.assigned_to_id(for each task) - Query N+1:
SELECT name FROM stores_store WHERE id = task.store_id(for each task)
Solution:
queryset = Task.objects.select_related(
'assigned_to', 'completed_by', 'store'
).all()
Result: Single query with SQL JOINs
Retrieve Endpoint Already Optimized
Pattern: Service handles optimization
# From services.py:22-24
task = Task.objects.select_related(
'assigned_to', 'completed_by', 'store'
).get(task_id=task_id)
Usage Examples
List Tasks for Store
from rest_framework.test import APIClient
client = APIClient()
client.credentials(HTTP_AUTHORIZATION='Bearer <jwt_token>')
response = client.get('/api/tasks/')
tasks = response.json()
for task in tasks:
print(f"{task['task_id']}: {task['title']} ({task['status']})")
Create Task
task_data = {
'title': 'Emergency cleanup',
'store': 'store-uuid',
'priority': 'high',
'status': 'pending'
}
response = client.post('/api/tasks/', data=task_data, format='json')
new_task = response.json()
print(f"Created task: {new_task['task_id']}")
Get Task Details
response = client.get('/api/tasks/task_12345/')
task_details = response.json()
if task_details['is_overdue']:
print(f"OVERDUE: {task_details['title']}")
if task_details['assigned_to']:
print(f"Assigned to: {task_details['assigned_to']['name']}")
Update Task Status
response = client.patch(
'/api/tasks/task_12345/',
data={'status': 'completed'},
format='json'
)
updated_task = response.json()
print(f"Task status: {updated_task['status']}")
Integration Points
Dependencies
Models:
# From views.py:5-6
from .models import Task, DeliverySchedule
Serializers:
# From views.py:6
from .serializers import TaskSerializer
Services:
# From views.py:7
from .services import TaskService
DRF Imports:
# From views.py:1-4
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
Called By
Frontend Applications:
- Dashboard (task management UI)
- POS terminals (task assignment/completion)
- Mobile apps (employee task lists)
Background Jobs:
- Scheduled task creation (e.g., daily cleaning tasks)
- Task reminder notifications
- Overdue task alerts
Related Documentation
- models.md - Task and DeliverySchedule models
- serializers.md - TaskSerializer data validation
- services.md - TaskService business logic
- Users Views - Employee API endpoints
- Stores Views - Store API endpoints