Django Expert
Expert guidance for Django - high-level Python web framework for building secure, scalable web applications with batteries included.
Core Concepts
Django Architecture
-
MVT (Model-View-Template) pattern
-
ORM (Object-Relational Mapping)
-
Admin interface
-
Authentication system
-
URL routing
-
Template engine
-
Forms and validation
Key Components
-
Models (database tables)
-
Views (business logic)
-
Templates (presentation)
-
URLs (routing)
-
Forms (user input)
-
Middleware (request/response processing)
Project Setup
Install Django
pip install django
Create project
django-admin startproject myproject cd myproject
Create app
python manage.py startapp myapp
Run migrations
python manage.py migrate
Create superuser
python manage.py createsuperuser
Run server
python manage.py runserver
Models
myapp/models.py
from django.db import models from django.contrib.auth.models import User from django.utils import timezone
class Post(models.Model): STATUS_CHOICES = [ ('draft', 'Draft'), ('published', 'Published'), ]
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
content = models.TextField()
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
published_at = models.DateTimeField(null=True, blank=True)
class Meta:
ordering = ['-created_at']
indexes = [
models.Index(fields=['-created_at']),
models.Index(fields=['slug']),
]
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if self.status == 'published' and not self.published_at:
self.published_at = timezone.now()
super().save(*args, **kwargs)
class Comment(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments') author = models.ForeignKey(User, on_delete=models.CASCADE) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_at']
def __str__(self):
return f'Comment by {self.author} on {self.post}'
Views
myapp/views.py
from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required from django.views.generic import ListView, DetailView, CreateView from django.contrib import messages from .models import Post, Comment from .forms import PostForm, CommentForm
Function-based view
def post_list(request): posts = Post.objects.filter(status='published').select_related('author') return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, slug): post = get_object_or_404(Post, slug=slug, status='published') comments = post.comments.select_related('author')
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.author = request.user
comment.save()
messages.success(request, 'Comment added successfully')
return redirect('post_detail', slug=slug)
else:
form = CommentForm()
return render(request, 'blog/post_detail.html', {
'post': post,
'comments': comments,
'form': form
})
Class-based views
class PostListView(ListView): model = Post template_name = 'blog/post_list.html' context_object_name = 'posts' paginate_by = 10
def get_queryset(self):
return Post.objects.filter(status='published').select_related('author')
class PostDetailView(DetailView): model = Post template_name = 'blog/post_detail.html' context_object_name = 'post'
def get_queryset(self):
return Post.objects.filter(status='published')
class PostCreateView(CreateView): model = Post form_class = PostForm template_name = 'blog/post_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
URLs
myproject/urls.py
from django.contrib import admin from django.urls import path, include
urlpatterns = [ path('admin/', admin.site.urls), path('', include('myapp.urls')), ]
myapp/urls.py
from django.urls import path from . import views
app_name = 'blog'
urlpatterns = [ path('', views.PostListView.as_view(), name='post_list'), path('post/<slug:slug>/', views.post_detail, name='post_detail'), path('post/create/', views.PostCreateView.as_view(), name='post_create'), ]
Forms
myapp/forms.py
from django import forms from .models import Post, Comment
class PostForm(forms.ModelForm): class Meta: model = Post fields = ['title', 'slug', 'content', 'status'] widgets = { 'content': forms.Textarea(attrs={'rows': 10}), }
def clean_slug(self):
slug = self.cleaned_data['slug']
if Post.objects.filter(slug=slug).exists():
raise forms.ValidationError('Slug already exists')
return slug
class CommentForm(forms.ModelForm): class Meta: model = Comment fields = ['content'] widgets = { 'content': forms.Textarea(attrs={'rows': 3}), }
Django REST Framework
Install: pip install djangorestframework
settings.py
INSTALLED_APPS = [ ... 'rest_framework', ]
serializers.py
from rest_framework import serializers from .models import Post, Comment
class CommentSerializer(serializers.ModelSerializer): author = serializers.StringRelatedField()
class Meta:
model = Comment
fields = ['id', 'author', 'content', 'created_at']
class PostSerializer(serializers.ModelSerializer): author = serializers.StringRelatedField() comments = CommentSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ['id', 'title', 'slug', 'author', 'content',
'status', 'created_at', 'comments']
views.py (API)
from rest_framework import viewsets, permissions from rest_framework.decorators import action from rest_framework.response import Response
class PostViewSet(viewsets.ModelViewSet): queryset = Post.objects.filter(status='published') serializer_class = PostSerializer permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
@action(detail=True, methods=['post'])
def publish(self, request, pk=None):
post = self.get_object()
post.status = 'published'
post.save()
return Response({'status': 'published'})
urls.py (API)
from rest_framework.routers import DefaultRouter
router = DefaultRouter() router.register(r'posts', PostViewSet)
urlpatterns = [ path('api/', include(router.urls)), ]
Admin Interface
myapp/admin.py
from django.contrib import admin from .models import Post, Comment
@admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display = ['title', 'author', 'status', 'created_at'] list_filter = ['status', 'created_at'] search_fields = ['title', 'content'] prepopulated_fields = {'slug': ('title',)} date_hierarchy = 'created_at'
actions = ['make_published']
def make_published(self, request, queryset):
queryset.update(status='published')
make_published.short_description = "Mark selected as published"
@admin.register(Comment) class CommentAdmin(admin.ModelAdmin): list_display = ['post', 'author', 'created_at'] list_filter = ['created_at'] search_fields = ['content']
Authentication
views.py
from django.contrib.auth import login, logout from django.contrib.auth.forms import UserCreationForm
def signup(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): user = form.save() login(request, user) return redirect('home') else: form = UserCreationForm() return render(request, 'registration/signup.html', {'form': form})
URLs
from django.contrib.auth import views as auth_views
urlpatterns = [ path('login/', auth_views.LoginView.as_view(), name='login'), path('logout/', auth_views.LogoutView.as_view(), name='logout'), path('signup/', signup, name='signup'), ]
Testing
myapp/tests.py
from django.test import TestCase, Client from django.contrib.auth.models import User from .models import Post
class PostModelTest(TestCase): def setUp(self): self.user = User.objects.create_user(username='test', password='test') self.post = Post.objects.create( title='Test Post', slug='test-post', author=self.user, content='Test content', status='published' )
def test_post_creation(self):
self.assertEqual(self.post.title, 'Test Post')
self.assertEqual(str(self.post), 'Test Post')
def test_post_slug_unique(self):
with self.assertRaises(Exception):
Post.objects.create(
title='Another Post',
slug='test-post', # Duplicate slug
author=self.user,
content='Content'
)
class PostViewTest(TestCase): def setUp(self): self.client = Client() self.user = User.objects.create_user(username='test', password='test')
def test_post_list_view(self):
response = self.client.get('/')
self.assertEqual(response.status_code, 200)
def test_post_create_requires_auth(self):
response = self.client.get('/post/create/')
self.assertEqual(response.status_code, 302) # Redirect to login
self.client.login(username='test', password='test')
response = self.client.get('/post/create/')
self.assertEqual(response.status_code, 200)
Best Practices
-
Use select_related/prefetch_related to avoid N+1 queries
-
Implement proper validation in forms
-
Use class-based views for common patterns
-
Protect against CSRF attacks (enabled by default)
-
Use Django's built-in authentication
-
Implement proper permission checks
-
Add database indexes for frequently queried fields
-
Use Django migrations for schema changes
Resources
-
Django Docs: https://docs.djangoproject.com/
-
Django REST Framework: https://www.django-rest-framework.org/
-
Django Packages: https://djangopackages.org/