Views are the heart of Django — they process HTTP requests and return HTTP responses. URLs map web addresses to views.
URL Configuration
Django uses URL patterns to route requests. Each URL pattern maps a URL to a view function.
Python
# posts/urls.py (create this file)
from django.urls import path
from . import views
app_name = 'posts' # Namespacing for {% url 'posts:list' %}
urlpatterns = [
path('', views.post_list, name='list'),
path('create/', views.post_create, name='create'),
path('<slug:slug>/', views.post_detail, name='detail'),
path('<slug:slug>/edit/', views.post_edit, name='edit'),
path('<slug:slug>/delete/', views.post_delete, name='delete'),
]Python
# myblog/urls.py (root URL config)
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),
path('posts/', include('posts.urls', namespace='posts')),
]Function-Based Views
Python
# posts/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .models import Post, Comment
from .forms import PostForm, CommentForm
def post_list(request):
"""Show all published posts."""
posts = Post.objects.filter(published=True).select_related('author')
return render(request, 'posts/post_list.html', {'posts': posts})
def post_detail(request, slug):
"""Show a single post with comments."""
post = get_object_or_404(Post, slug=slug, published=True)
comments = post.comments.filter(active=True)
if request.method == 'POST':
comment_form = CommentForm(request.POST)
if comment_form.is_valid():
comment = comment_form.save(commit=False)
comment.post = post
comment.author = request.user
comment.save()
messages.success(request, 'Comment added!')
return redirect('posts:detail', slug=slug)
else:
comment_form = CommentForm()
return render(request, 'posts/post_detail.html', {
'post': post,
'comments': comments,
'comment_form': comment_form,
})
@login_required
def post_create(request):
"""Create a new post (authenticated users only)."""
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
form.save_m2m() # Save tags
messages.success(request, 'Post created!')
return redirect('posts:detail', slug=post.slug)
else:
form = PostForm()
return render(request, 'posts/post_form.html', {'form': form, 'action': 'Create'})
@login_required
def post_edit(request, slug):
"""Edit a post (author only)."""
post = get_object_or_404(Post, slug=slug, author=request.user)
if request.method == 'POST':
form = PostForm(request.POST, instance=post)
if form.is_valid():
form.save()
messages.success(request, 'Post updated!')
return redirect('posts:detail', slug=post.slug)
else:
form = PostForm(instance=post)
return render(request, 'posts/post_form.html', {'form': form, 'action': 'Edit'})
@login_required
def post_delete(request, slug):
"""Delete a post (author only)."""
post = get_object_or_404(Post, slug=slug, author=request.user)
if request.method == 'POST':
post.delete()
messages.success(request, 'Post deleted!')
return redirect('posts:list')
return render(request, 'posts/post_confirm_delete.html', {'post': post})Class-Based Views (Alternative)
Django provides class-based views for common patterns:
Python
from django.views.generic import ListView, DetailView, CreateView, UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'posts/post_list.html'
context_object_name = 'posts'
paginate_by = 10
def get_queryset(self):
return Post.objects.filter(published=True).select_related('author')
class PostDetailView(DetailView):
model = Post
template_name = 'posts/post_detail.html'
slug_field = 'slug'
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'slug', 'body', 'tags']
template_name = 'posts/post_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)FBV vs CBV Comparison
| Aspect | Function-Based | Class-Based |
|---|---|---|
| Readability | Explicit, easy to follow | More abstract |
| Reuse | Via decorators | Via mixins and inheritance |
| Simplicity | Simple for simple views | Complex for simple views |
| Testing | Test the function directly | Use Django's test utilities |
| When to use | Custom logic | CRUD patterns |
URL Dispatch Flow
Code
Request: GET /posts/my-first-post/
1. Root urls.py matches: 'posts/' → include('posts.urls')
2. posts/urls.py matches: '<slug:slug>/', slug='my-first-post'
3. Calls views.post_detail(request, slug='my-first-post')
4. View returns HttpResponse with rendered HTML
5. Django sends the response back to the browser💡 Tip: Use
select_related('author')on querysets that access ForeignKey fields to avoid N+1 database queries.