Challenge 1: Write a Background Task — Possible Solution ==================================================================== # catalog/tasks.py from celery import shared_task @shared_task def generate_book_report(book_id): from .models import Book book = Book.objects.get(pk=book_id) # re-fetched fresh inside the task print(f"Report: '{book.title}' — ${book.price}, in_stock={book.in_stock}") # catalog/views.py from django.shortcuts import get_object_or_404, redirect from .models import Book from .tasks import generate_book_report def request_book_report(request, book_id): book = get_object_or_404(Book, pk=book_id) generate_book_report.delay(book.pk) # pass the ID, not `book` itself return redirect("book-detail", book_id=book.pk) WHY THIS WORKS -------------- - generate_book_report(book_id) accepts a plain integer (or whatever type the primary key is) rather than a Book instance — integers serialize to JSON trivially when Celery sends the task message to the broker, unlike a full Django model instance. - The task re-fetches the book with Book.objects.get(pk=book_id) INSIDE the task body, at the moment the worker actually runs it — this guarantees the data reflects the book's current state at execution time, not whatever it looked like back when .delay() was called in the view. - The view calls generate_book_report.delay(book.pk) — passing book.pk (just the ID) rather than book itself — and returns a redirect immediately without waiting for the task to actually run; the report generation happens later, on a separate worker process, completely decoupled from this request/response cycle.