As a backend developer, you likely spend hours writing boilerplate Django code: models, serializers, views, admin configurations. AI tools like ChatGPT can cut this time by 70-80% if you know the right prompts. Based on my daily workflow (and input from the Django community on Reddit and Stack Overflow), I've compiled 12 battle-tested prompts that cover the full Django stack. Each prompt includes a real usage example and the exact output you can expect.
1. Generate a Django Model with All Fields and Methods
Prompt:
"Create a Django model for a BlogPost with the following fields: title (unique, max 200 chars), slug (auto-populated from title), content (RichTextField), author (ForeignKey to User), tags (ManyToManyField to Tag), status (draft/published), published_at (nullable DateTimeField). Add a __str__ method, get_absolute_url, and a custom manager that filters published posts."
Why it works: This prompt specifies field types, constraints, and custom methods. You get production-ready code.
Example output:
from django.db import models
from django.utils.text import slugify
from django.urls import reverse
from django.contrib.auth.models import User
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='published')
class BlogPost(models.Model):
STATUS_CHOICES = [
('draft', 'Draft'),
('published', 'Published'),
]
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(unique=True, blank=True)
content = models.TextField() # or use RichTextField from ckeditor
author = models.ForeignKey(User, on_delete=models.CASCADE)
tags = models.ManyToManyField('Tag', blank=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
published_at = models.DateTimeField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = models.Manager()
published = PublishedManager()
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super().save(*args, **kwargs)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:detail', kwargs={'slug': self.slug})
2. Add Timestamps and Soft Delete to All Models
Prompt:
"Create a Django abstract base model TimeStampedModel with created_at and updated_at fields (auto-populated). Also create SoftDeleteModel with is_deleted and deleted_at. Both should be abstract. Show how to inherit them in a model."
Why it works: Mixins reduce repetition. This pattern is used by many production Django projects (e.g., Django REST Framework's own models).
Example output:
from django.db import models
from django.utils import timezone
class TimeStampedModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class SoftDeleteModel(models.Model):
is_deleted = models.BooleanField(default=False)
deleted_at = models.DateTimeField(null=True, blank=True)
def soft_delete(self):
self.is_deleted = True
self.deleted_at = timezone.now()
self.save()
class Meta:
abstract = True
class Product(TimeStampedModel, SoftDeleteModel):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
3. Build a Django Form with Validation and Widgets
Prompt:
"Create a Django ModelForm for the BlogPost model above. Include fields: title, content, tags, status. Add custom validation: title must not contain the word 'test'. Use a RichText widget for content (from django-ckeditor). Show the form in a view."
Why it works: Specifying validation and widgets saves you from writing multiple form classes.
Example output:
from django import forms
from ckeditor.widgets import CKEditorWidget
from .models import BlogPost
class BlogPostForm(forms.ModelForm):
content = forms.CharField(widget=CKEditorWidget())
class Meta:
model = BlogPost
fields = ['title', 'content', 'tags', 'status']
def clean_title(self):
title = self.cleaned_data['title']
if 'test' in title.lower():
raise forms.ValidationError("Title cannot contain 'test'")
return title
4. Create a CRUD ViewSet with Django REST Framework
Prompt:
"Create a Django REST Framework ViewSet for the BlogPost model. Include list, create, retrieve, update, and partial update actions. Use a ModelSerializer that shows all fields except created_at and updated_at. Add permission: only authenticated users can create/update. Add search by title and tags."
Why it works: This prompt covers serializers, views, permissions, and filtering in one request.
Example output:
from rest_framework import viewsets, permissions, filters
from rest_framework.pagination import PageNumberPagination
from .models import BlogPost
from .serializers import BlogPostSerializer
class BlogPostViewSet(viewsets.ModelViewSet):
queryset = BlogPost.published.all()
serializer_class = BlogPostSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
filter_backends = [filters.SearchFilter]
search_fields = ['title', 'tags__name']
pagination_class = PageNumberPagination
5. Write a Custom Django Management Command
Prompt:
"Create a Django management command publish_scheduled_posts that runs every hour via cron. It should find all BlogPosts with status='draft' and created_at older than 24 hours, then publish them (set status to 'published' and published_at to now). Add verbose output."
Why it works: Scheduled tasks are common in web apps. This prompt gives you a ready-to-use command.
Example output:
from django.core.management.base import BaseCommand
from django.utils import timezone
from datetime import timedelta
from blog.models import BlogPost
class Command(BaseCommand):
help = 'Publishes blog posts that are older than 24 hours'
def handle(self, *args, **options):
cutoff = timezone.now() - timedelta(hours=24)
posts = BlogPost.objects.filter(
status='draft',
created_at__lte=cutoff,
published_at__isnull=True
)
count = 0
for post in posts:
post.status = 'published'
post.published_at = timezone.now()
post.save()
count += 1
self.stdout.write(self.style.SUCCESS(f'Published: {post.title}'))
self.stdout.write(self.style.SUCCESS(f'Total published: {count}'))
6. Set Up Django Admin with Custom Filters and Actions
Prompt:
"Customize the Django admin for BlogPost. Add list display: title, author, status, published_at. Add list filters for status and author (dropdown). Add search by title. Add a custom admin action 'Mark as featured'. Use fieldsets to group fields in the edit form."
Why it works: Admin customization is a daily task. This prompt covers filters, actions, and field grouping.
Example output:
from django.contrib import admin
from .models import BlogPost
@admin.action(description='Mark selected posts as featured')
def make_featured(modeladmin, request, queryset):
queryset.update(status='featured')
class BlogPostAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'status', 'published_at']
list_filter = ['status', 'author']
search_fields = ['title']
actions = [make_featured]
fieldsets = (
(None, {
'fields': ('title', 'slug', 'author')
}),
('Content', {
'fields': ('content', 'tags')
}),
('Publishing', {
'fields': ('status', 'published_at')
}),
)
prepopulated_fields = {'slug': ('title',)}
admin.site.register(BlogPost, BlogPostAdmin)
7. Write Unit Tests for Models and Views
Prompt:
"Write Django unit tests for the BlogPost model: test the __str__ method, test that published manager only returns published posts, test the save method creates slug. Also write a test for the API endpoint GET /api/posts/ that returns a 200 status and contains the expected fields."
Why it works: Testing is often neglected, but this prompt generates both model and view tests.
Example output:
from django.test import TestCase
from django.contrib.auth.models import User
from rest_framework.test import APITestCase
from .models import BlogPost
class BlogPostModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(username='testuser')
self.post = BlogPost.objects.create(
title='My Post',
content='Content',
author=self.user,
status='published'
)
def test_str_method(self):
self.assertEqual(str(self.post), 'My Post')
def test_published_manager(self):
draft = BlogPost.objects.create(
title='Draft',
content='Draft content',
author=self.user,
status='draft'
)
published_posts = BlogPost.published.all()
self.assertIn(self.post, published_posts)
self.assertNotIn(draft, published_posts)
class BlogPostAPITest(APITestCase):
def setUp(self):
self.user = User.objects.create_user(username='testuser')
self.client.force_authenticate(user=self.user)
def test_list_posts(self):
BlogPost.objects.create(
title='API Post',
content='Content',
author=self.user,
status='published'
)
response = self.client.get('/api/posts/')
self.assertEqual(response.status_code, 200)
self.assertIn('title', response.data['results'][0])
8. Implement Signal Handlers (e.g., Send Email on Post Publish)
Prompt:
"Create a Django signal that sends an email to the author when a BlogPost is published. Use post_save signal. Check if the status changed from draft to published to avoid sending on every save. Include a ready() method in the app config."
Why it works: Signals decouple logic. This prompt handles the common pitfall of sending duplicate emails.
Example output:
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.mail import send_mail
from .models import BlogPost
@receiver(post_save, sender=BlogPost)
def notify_author_on_publish(sender, instance, created, **kwargs):
if created:
return
old_instance = BlogPost.objects.get(pk=instance.pk)
if old_instance.status == 'draft' and instance.status == 'published':
send_mail(
'Your post is published!',
f'Your blog post "{instance.title}" is now live.',
'noreply@example.com',
[instance.author.email],
fail_silently=False,
)
9. Write a Django Middleware (e.g., Request Timing)
Prompt:
"Create a Django middleware that logs the time taken to process each request. Use process_request and process_response hooks. Log the URL and duration in seconds. Use Python's time module. Include the middleware in the settings example."
Why it works: Middleware is a core concept. This gives a practical example for debugging.
Example output:
import time
from django.utils.deprecation import MiddlewareMixin
class RequestTimingMiddleware(MiddlewareMixin):
def process_request(self, request):
request._start_time = time.time()
def process_response(self, request, response):
if hasattr(request, '_start_time'):
duration = time.time() - request._start_time
print(f"Request to {request.path} took {duration:.3f} seconds")
return response
# In settings.py:
# MIDDLEWARE = [
# ...,
# 'blog.middleware.RequestTimingMiddleware',
# ]
10. Build a Custom Template Tag for Recent Posts
Prompt:
"Create a Django inclusion template tag recent_posts that returns the 5 most recent published BlogPosts. Use it in a sidebar. Include the template file blog/recent_posts.html that renders a simple unordered list."
Why it works: Template tags are essential for reusable UI components.
Example output:
from django import template
from blog.models import BlogPost
register = template.Library()
@register.inclusion_tag('blog/recent_posts.html')
def recent_posts(count=5):
posts = BlogPost.published.order_by('-published_at')[:count]
return {'posts': posts}
Template blog/recent_posts.html:
<ul>
{% for post in posts %}
<li><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></li>
{% empty %}
<li>No recent posts.</li>
{% endfor %}
</ul>
11. Write a Django Query Optimization Using select_related and prefetch_related
Prompt:
"Optimize a Django view that lists all BlogPosts with their authors and tags. Use select_related for the author ForeignKey and prefetch_related for the tags ManyToManyField. Show the original queryset and the optimized one. Explain the reduction in SQL queries."
Why it works: N+1 queries are a common performance issue. This prompt teaches optimization.
Example output:
# Without optimization (N+1 queries):
posts = BlogPost.objects.all() # 1 query
for post in posts:
print(post.author.username) # N additional queries
# With optimization (2 queries total):
posts = BlogPost.objects.select_related('author').prefetch_related('tags').all()
for post in posts:
print(post.author.username) # no extra query
12. Generate a Full Django REST API with Authentication (JWT)
Prompt:
"Create a complete Django REST API for a simple blog. Include: User registration (POST /api/register/), login (POST /api/token/), and CRUD for BlogPost. Use JWT authentication from djangorestframework-simplejwt. Protect create/update/delete endpoints. Return 201 for creation and 403 for unauthorized access."
Why it works: This prompt combines multiple concepts: serializers, views, authentication, and endpoints.
Example output:
# serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import BlogPost
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ['username', 'email', 'password']
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
return user
class BlogPostSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPost
fields = '__all__'
read_only_fields = ['author', 'created_at', 'updated_at']
# views.py
from rest_framework import generics, permissions
from rest_framework.permissions import IsAuthenticated
from .serializers import RegisterSerializer, BlogPostSerializer
from .models import BlogPost
class RegisterView(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = RegisterSerializer
permission_classes = [permissions.AllowAny]
class BlogPostListCreateView(generics.ListCreateAPIView):
queryset = BlogPost.published.all()
serializer_class = BlogPostSerializer
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
# urls.py
from django.urls import path
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path('api/register/', RegisterView.as_view()),
path('api/token/', TokenObtainPairView.as_view()),
path('api/token/refresh/', TokenRefreshView.as_view()),
path('api/posts/', BlogPostListCreateView.as_view()),
]
Conclusion
These 12 prompts cover the most common Django tasks: models, forms, admin, REST APIs, testing, signals, middleware, template tags, and query optimization. By using them in your daily workflow, you can reduce boilerplate writing by up to 80% and focus on the unique business logic of your application. Start by copying the prompts that match your current task, then customize the output to fit your project's naming conventions and requirements. The key is to be specific in your prompts: mention field types, constraints, and desired behavior. Happy coding!
Comments