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
Orderwith fields:user(ForeignKey),total(DecimalField),status(CharField choices: pending, paid, shipped, cancelled). Add a custom manager methodrecent_orders()that returns orders created within the last 7 days. Overridesave()to settotalto 0.0 if negative. Include a@propertyforis_overduethat 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
ProductListViewthat inherits fromListViewandSearchMixin. It should show 20 products per page, allow filtering bycategoryquery parameter, and search acrossnameanddescription. Overrideget_querysetto apply filters. Also create aProductDetailViewusingDetailViewthat increments aviews_countfield 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
OrderSerializerthat includes nestedOrderItemSerializer. TheOrderItemSerializermust validate thatquantityis at least 1 andpriceis positive. Overridecreate()inOrderSerializerto create the order and its items in a single transaction. Useserializers.SerializerMethodFieldto exposetotal_itemscount."
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_relatedforForeignKeyfields andprefetch_relatedforManyToManyField. Add aPrefetchobject to filter relatedreviewsonly 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
OrderAdminclass that showsuser,total,status,created_atin list view. Add amark_as_paidaction. Uselist_filterforstatusandcreated_at. Addsearch_fieldsfor user email. IncludeOrderItemInlinewithproduct,quantity,pricefields. Overrideget_querysetto annotateitem_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_savesignal forOrderthat checks if status changed to 'paid' and sends an email to the user. Useupdate_fieldsto avoid infinite loops. Also create apre_savesignal 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
OrderViewSetwith standard CRUD plus a@actiondecorator forcancelthat changes status to 'cancelled' and refunds via a mock function. Add another actionsummarythat 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
Ordermodel: a composite index on(user, status)for user order lookups, a partial index onstatus='pending'for admin queues, and adb_indexoncreated_at. UseIndexfromdjango.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_ordersthat deletes orders with status 'cancelled' older than 30 days. Add a--dry-runflag to preview without deleting. Usestdout.writefor 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
IsOwnerOrAdminthat only allows the order owner or staff to access. Then create aThrottledOrderViewSetthat usesScopedRateThrottlewith 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!
Comments