10 Prompts for Django: From Models to REST API Like a Senior Developer

Why You Need a Prompt Arsenal for Django

Django is a high-level Python web framework that powers some of the busiest sites on the internet, including Instagram, Pinterest, and Disqus. According to the 2025 JetBrains Developer Survey, Django remains the second most popular Python web framework, used by 22% of Python developers. But even experienced developers hit repetitive tasks — writing verbose serializers, debugging N+1 queries, or setting up admin configurations. A well-crafted prompt acts as a force multiplier, letting you focus on architecture rather than boilerplate.

This collection of 10 prompts covers the Django stack: models, views, API endpoints, and admin customization. Each prompt is battle-tested and comes with a concrete example you can copy into your project today.


1. Model with Business Logic (Not Just a Dumb Container)

Task: Create a Django model that encapsulates business logic — validation, computed properties, and query scoping — instead of just fields.

Prompt:

"Generate a Django model called Order with fields: user (ForeignKey), total (DecimalField), status (CharField choices: pending, paid, shipped, cancelled). Add a custom manager method recent_orders() that returns orders created within the last 7 days. Override save() to set total to 0.0 if negative. Include a @property for is_overdue that checks if status is 'pending' and created more than 3 days ago."

Example result:

from django.db import models
from django.utils import timezone
from datetime import timedelta

class OrderManager(models.Manager):
    def recent_orders(self):
        seven_days_ago = timezone.now() - timedelta(days=7)
        return self.get_queryset().filter(created_at__gte=seven_days_ago)

class Order(models.Model):
    STATUS_CHOICES = [
        ('pending', 'Pending'),
        ('paid', 'Paid'),
        ('shipped', 'Shipped'),
        ('cancelled', 'Cancelled'),
    ]
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    total = models.DecimalField(max_digits=10, decimal_places=2)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
    created_at = models.DateTimeField(auto_now_add=True)

    objects = OrderManager()

    def save(self, *args, **kwargs):
        if self.total < 0:
            self.total = 0.0
        super().save(*args, **kwargs)

    @property
    def is_overdue(self):
        if self.status == 'pending' and self.created_at:
            return timezone.now() - self.created_at > timedelta(days=3)
        return False

2. Class-Based View with Mixins for Dry Code

Task: Build a class-based view (CBV) using Django's built-in mixins to handle a list and detail view with pagination and search.

Prompt:

"Write a Django class-based view ProductListView that inherits from ListView and SearchMixin. It should show 20 products per page, allow filtering by category query parameter, and search across name and description. Override get_queryset to apply filters. Also create a ProductDetailView using DetailView that increments a views_count field on each access."

Example result:

from django.views.generic import ListView, DetailView
from django.db.models import Q

class ProductListView(ListView):
    model = Product
    template_name = 'products/list.html'
    paginate_by = 20

    def get_queryset(self):
        qs = super().get_queryset()
        category = self.request.GET.get('category')
        search = self.request.GET.get('q')
        if category:
            qs = qs.filter(category__slug=category)
        if search:
            qs = qs.filter(Q(name__icontains=search) | Q(description__icontains=search))
        return qs

class ProductDetailView(DetailView):
    model = Product
    template_name = 'products/detail.html'

    def get_object(self, queryset=None):
        obj = super().get_object(queryset)
        obj.views_count += 1
        obj.save(update_fields=['views_count'])
        return obj

3. DRF Serializer with Nested Validation

Task: Create a Django REST Framework (DRF) serializer that handles nested write operations and custom validation.

Prompt:

"Generate a DRF serializer OrderSerializer that includes nested OrderItemSerializer. The OrderItemSerializer must validate that quantity is at least 1 and price is positive. Override create() in OrderSerializer to create the order and its items in a single transaction. Use serializers.SerializerMethodField to expose total_items count."

Example result:

from rest_framework import serializers
from django.db import transaction

class OrderItemSerializer(serializers.Serializer):
    product_id = serializers.IntegerField()
    quantity = serializers.IntegerField(min_value=1)
    price = serializers.DecimalField(max_digits=10, decimal_places=2)

    def validate_price(self, value):
        if value <= 0:
            raise serializers.ValidationError("Price must be positive.")
        return value

class OrderSerializer(serializers.ModelSerializer):
    items = OrderItemSerializer(many=True, write_only=True)
    total_items = serializers.SerializerMethodField()

    class Meta:
        model = Order
        fields = ['id', 'user', 'total', 'status', 'items', 'total_items']

    def get_total_items(self, obj):
        return obj.items.count()

    @transaction.atomic
    def create(self, validated_data):
        items_data = validated_data.pop('items')
        order = Order.objects.create(**validated_data)
        for item_data in items_data:
            OrderItem.objects.create(order=order, **item_data)
        return order

4. Optimize Queries with select_related and prefetch_related

Task: Write a view that avoids the N+1 query problem by using Django's ORM optimization methods.

Prompt:

"Rewrite this view to eliminate N+1 queries. Use select_related for ForeignKey fields and prefetch_related for ManyToManyField. Add a Prefetch object to filter related reviews only those with rating >= 4."

Before (N+1 problem):

def product_list(request):
    products = Product.objects.all()
    for p in products:
        print(p.category.name)  # Hits DB each time
        print([r.text for r in p.reviews.all()])  # Another query per product

After (optimized):

from django.db.models import Prefetch

def product_list(request):
    top_reviews = Prefetch('reviews', queryset=Review.objects.filter(rating__gte=4))
    products = Product.objects.select_related('category').prefetch_related(top_reviews)
    for p in products:
        print(p.category.name)  # No extra query
        print([r.text for r in p.reviews.all()])  # Prefetched

5. Custom Admin for Better UX

Task: Customize Django admin to display inline models, search, filters, and actions.

Prompt:

"Create an OrderAdmin class that shows user, total, status, created_at in list view. Add a mark_as_paid action. Use list_filter for status and created_at. Add search_fields for user email. Include OrderItemInline with product, quantity, price fields. Override get_queryset to annotate item_count."

Example result:

from django.contrib import admin
from django.db.models import Count

class OrderItemInline(admin.TabularInline):
    model = OrderItem
    extra = 1
    fields = ('product', 'quantity', 'price')

@admin.action(description='Mark selected orders as paid')
def mark_as_paid(modeladmin, request, queryset):
    queryset.update(status='paid')

class OrderAdmin(admin.ModelAdmin):
    list_display = ('id', 'user', 'total', 'status', 'created_at', 'item_count')
    list_filter = ('status', 'created_at')
    search_fields = ('user__email',)
    actions = [mark_as_paid]
    inlines = [OrderItemInline]

    def get_queryset(self, request):
        return super().get_queryset(request).annotate(item_count=Count('items'))

    @admin.display(description='Items')
    def item_count(self, obj):
        return obj.item_count

admin.site.register(Order, OrderAdmin)

6. Django Signals for Side Effects

Task: Use Django signals to trigger side effects — like sending a notification when an order is paid.

Prompt:

"Write a post_save signal for Order that checks if status changed to 'paid' and sends an email to the user. Use update_fields to avoid infinite loops. Also create a pre_save signal that logs every status change."

Example result:

from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.core.mail import send_mail

@receiver(pre_save, sender=Order)
def log_status_change(sender, instance, **kwargs):
    if instance.pk:
        old = sender.objects.get(pk=instance.pk)
        if old.status != instance.status:
            print(f"Order {instance.pk}: {old.status} -> {instance.status}")

@receiver(post_save, sender=Order)
def notify_paid_order(sender, instance, created, **kwargs):
    if not created and instance.status == 'paid':
        send_mail(
            'Order Paid',
            f'Your order {instance.id} has been paid.',
            'from@example.com',
            [instance.user.email],
            fail_silently=True,
        )

7. DRF ViewSet with Custom Actions

Task: Build a DRF ViewSet with custom actions for business operations.

Prompt:

"Create a OrderViewSet with standard CRUD plus a @action decorator for cancel that changes status to 'cancelled' and refunds via a mock function. Add another action summary that returns aggregated data (total orders, revenue) for the current user."

Example result:

from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django.db.models import Sum, Count

class OrderViewSet(viewsets.ModelViewSet):
    queryset = Order.objects.all()
    serializer_class = OrderSerializer

    @action(detail=True, methods=['post'])
    def cancel(self, request, pk=None):
        order = self.get_object()
        if order.status in ['paid', 'pending']:
            order.status = 'cancelled'
            order.save()
            # mock_refund(order)
            return Response({'status': 'cancelled'})
        return Response({'error': 'Cannot cancel'}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=['get'])
    def summary(self, request):
        qs = self.get_queryset().filter(user=request.user)
        data = qs.aggregate(
            total_orders=Count('id'),
            total_revenue=Sum('total')
        )
        return Response(data)

8. Database Indexing Strategy for Performance

Task: Write a model with optimal indexes for common query patterns.

Prompt:

"Add database indexes to the Order model: a composite index on (user, status) for user order lookups, a partial index on status='pending' for admin queues, and a db_index on created_at. Use Index from django.db.models."

Example result:

from django.db import models
from django.db.models import Index

class Order(models.Model):
    # ... fields
    class Meta:
        indexes = [
            Index(fields=['user', 'status'], name='idx_user_status'),
            Index(fields=['created_at']),
        ]

To add a partial index (PostgreSQL only), use Condition:

from django.db.models import Q, Index

class Meta:
    indexes = [
        Index(
            fields=['created_at'],
            condition=Q(status='pending'),
            name='idx_pending_orders'
        ),
    ]

9. Django Management Command for Data Cleanup

Task: Write a custom management command to automate periodic tasks.

Prompt:

"Create a management command cleanup_old_orders that deletes orders with status 'cancelled' older than 30 days. Add a --dry-run flag to preview without deleting. Use stdout.write for output."

Example result:

# management/commands/cleanup_old_orders.py
from django.core.management.base import BaseCommand
from django.utils import timezone
from datetime import timedelta
from myapp.models import Order

class Command(BaseCommand):
    help = 'Delete cancelled orders older than 30 days'

    def add_arguments(self, parser):
        parser.add_argument('--dry-run', action='store_true', help='Print count without deleting')

    def handle(self, *args, **options):
        cutoff = timezone.now() - timedelta(days=30)
        qs = Order.objects.filter(status='cancelled', created_at__lt=cutoff)
        count = qs.count()
        if options['dry_run']:
            self.stdout.write(f"Would delete {count} orders")
        else:
            qs.delete()
            self.stdout.write(f"Deleted {count} orders")

Run with: python manage.py cleanup_old_orders --dry-run


10. API Throttling and Permissions

Task: Configure DRF throttling and custom permissions for an API endpoint.

Prompt:

"Write a custom permission class IsOwnerOrAdmin that only allows the order owner or staff to access. Then create a ThrottledOrderViewSet that uses ScopedRateThrottle with 10 requests per hour for anonymous users and 100 for authenticated."

Example result:

from rest_framework.permissions import BasePermission, SAFE_METHODS

class IsOwnerOrAdmin(BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj.user == request.user or request.user.is_staff

# In settings.py:
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.ScopedRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'orders': '100/hour',
        'orders_anon': '10/hour',
    },
}

class ThrottledOrderViewSet(viewsets.ModelViewSet):
    queryset = Order.objects.all()
    serializer_class = OrderSerializer
    permission_classes = [IsOwnerOrAdmin]
    throttle_scope = 'orders'

    def get_throttles(self):
        if not self.request.user.is_authenticated:
            self.throttle_scope = 'orders_anon'
        return super().get_throttles()

Conclusion

These 10 prompts cover the daily challenges of a Django backend developer: modeling business logic, optimizing queries, customizing the admin, building REST APIs with DRF, and automating tasks. The key is to treat prompts as templates — adapt them to your project's specific needs.

Start by copying the examples, then experiment: add your own validation rules, combine multiple prompts into one, or chain them for a complete feature (e.g., model + serializer + viewset + admin). If you found this useful, share it with your team or bookmark it for your next sprint. Happy coding!

← All posts

Comments