Views
Function-Based Views
Basic Function Views
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse, JsonResponse, Http404
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods
from .models import Article, Category
def home(request):
articles = Article.objects.filter(published=True)[:5]
return render(request, 'home.html', {'articles': articles})
def article_detail(request, slug):
article = get_object_or_404(Article, slug=slug, published=True)
return render(request, 'article_detail.html', {'article': article})
@login_required
def create_article(request):
if request.method == 'POST':
title = request.POST.get('title')
content = request.POST.get('content')
article = Article.objects.create(
title=title,
content=content,
author=request.user
)
return redirect('article_detail', slug=article.slug)
return render(request, 'create_article.html')
@require_http_methods(["GET", "POST"])
def contact(request):
if request.method == 'POST':
# Process form
return redirect('contact_success')
return render(request, 'contact.html')
Request and Response Objects
def request_info(request):
# Request method
method = request.method
# GET parameters
search = request.GET.get('search', '')
page = request.GET.get('page', 1)
# POST data
if request.method == 'POST':
username = request.POST.get('username')
email = request.POST.get('email')
# Request headers
user_agent = request.META.get('HTTP_USER_AGENT')
ip_address = request.META.get('REMOTE_ADDR')
# Files
if request.FILES:
uploaded_file = request.FILES['file']
# User information
user = request.user
is_authenticated = request.user.is_authenticated
# Session data
request.session['last_visited'] = timezone.now().isoformat()
return render(request, 'request_info.html', {
'method': method,
'search': search,
'user_agent': user_agent,
})
JSON Views
import json
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
def api_articles(request):
if request.method == 'GET':
articles = Article.objects.filter(published=True)
data = []
for article in articles:
data.append({
'id': article.id,
'title': article.title,
'slug': article.slug,
'author': article.author.username,
'created_at': article.created_at.isoformat(),
})
return JsonResponse({'articles': data})
elif request.method == 'POST':
data = json.loads(request.body)
article = Article.objects.create(
title=data['title'],
content=data['content'],
author=request.user
)
return JsonResponse({
'id': article.id,
'title': article.title,
'slug': article.slug,
}, status=201)
@csrf_exempt
def api_article_detail(request, pk):
article = get_object_or_404(Article, pk=pk)
if request.method == 'GET':
return JsonResponse({
'id': article.id,
'title': article.title,
'content': article.content,
'author': article.author.username,
})
elif request.method == 'PUT':
data = json.loads(request.body)
article.title = data.get('title', article.title)
article.content = data.get('content', article.content)
article.save()
return JsonResponse({'success': True})
elif request.method == 'DELETE':
article.delete()
return JsonResponse({'success': True})
Class-Based Views
Generic Views
from django.views.generic import (
ListView, DetailView, CreateView, UpdateView, DeleteView
)
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
class ArticleListView(ListView):
model = Article
template_name = 'articles/list.html'
context_object_name = 'articles'
paginate_by = 10
def get_queryset(self):
return Article.objects.filter(published=True)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
class ArticleDetailView(DetailView):
model = Article
template_name = 'articles/detail.html'
context_object_name = 'article'
slug_field = 'slug'
slug_url_kwarg = 'slug'
def get_queryset(self):
return Article.objects.filter(published=True)
class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article
template_name = 'articles/create.html'
fields = ['title', 'content', 'category']
success_url = reverse_lazy('article_list')
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
template_name = 'articles/update.html'
fields = ['title', 'content', 'category']
def get_queryset(self):
return Article.objects.filter(author=self.request.user)
class ArticleDeleteView(LoginRequiredMixin, DeleteView):
model = Article
template_name = 'articles/delete.html'
success_url = reverse_lazy('article_list')
def get_queryset(self):
return Article.objects.filter(author=self.request.user)
Custom Class-Based Views
from django.views import View
from django.views.generic.base import TemplateView
class CustomView(View):
def get(self, request, *args, **kwargs):
return render(request, 'custom_template.html')
def post(self, request, *args, **kwargs):
# Handle POST request
return redirect('success_page')
class DashboardView(TemplateView):
template_name = 'dashboard.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user_articles'] = Article.objects.filter(
author=self.request.user
)
context['recent_articles'] = Article.objects.filter(
published=True
)[:5]
return context
Mixins
from django.contrib.auth.mixins import (
LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin
)
from django.views.generic import CreateView, UpdateView
class AuthorRequiredMixin(UserPassesTestMixin):
def test_func(self):
article = self.get_object()
return article.author == self.request.user
class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class ArticleUpdateView(LoginRequiredMixin, AuthorRequiredMixin, UpdateView):
model = Article
fields = ['title', 'content']
class AdminRequiredMixin(PermissionRequiredMixin):
permission_required = 'myapp.change_article'
def handle_no_permission(self):
return redirect('login')
class ArticleAdminView(AdminRequiredMixin, ListView):
model = Article
template_name = 'admin/articles.html'
View Decorators
Authentication Decorators
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.admin.views.decorators import staff_member_required
@login_required
def profile(request):
return render(request, 'profile.html')
@login_required(login_url='/custom-login/')
def dashboard(request):
return render(request, 'dashboard.html')
@permission_required('myapp.add_article')
def create_article(request):
return render(request, 'create_article.html')
@staff_member_required
def admin_panel(request):
return render(request, 'admin_panel.html')
HTTP Method Decorators
from django.views.decorators.http import (
require_http_methods, require_GET, require_POST, require_safe
)
@require_GET
def get_only_view(request):
return render(request, 'template.html')
@require_POST
def post_only_view(request):
# Process form
return redirect('success')
@require_http_methods(["GET", "POST"])
def get_post_view(request):
if request.method == 'GET':
return render(request, 'form.html')
else:
# Process POST
return redirect('success')
@require_safe # GET or HEAD only
def safe_view(request):
return render(request, 'template.html')
CSRF and Cache Decorators
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.views.decorators.cache import cache_page, never_cache
@csrf_exempt
def api_endpoint(request):
# API endpoint that doesn't require CSRF token
return JsonResponse({'message': 'success'})
@cache_page(60 * 15) # Cache for 15 minutes
def cached_view(request):
return render(request, 'template.html')
@never_cache
def fresh_view(request):
return render(request, 'template.html')
Form Handling
Form Processing
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import ArticleForm, ContactForm
def create_article(request):
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save(commit=False)
article.author = request.user
article.save()
messages.success(request, 'Article created successfully!')
return redirect('article_detail', slug=article.slug)
else:
form = ArticleForm()
return render(request, 'create_article.html', {'form': form})
def update_article(request, pk):
article = get_object_or_404(Article, pk=pk, author=request.user)
if request.method == 'POST':
form = ArticleForm(request.POST, instance=article)
if form.is_valid():
form.save()
messages.success(request, 'Article updated successfully!')
return redirect('article_detail', slug=article.slug)
else:
form = ArticleForm(instance=article)
return render(request, 'update_article.html', {
'form': form,
'article': article
})
File Upload Handling
def upload_file(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
document = form.save(commit=False)
document.uploaded_by = request.user
document.save()
return redirect('document_list')
else:
form = DocumentForm()
return render(request, 'upload.html', {'form': form})
def handle_multiple_files(request):
if request.method == 'POST':
files = request.FILES.getlist('files')
for file in files:
Document.objects.create(
file=file,
uploaded_by=request.user
)
messages.success(request, f'{len(files)} files uploaded successfully!')
return redirect('document_list')
return render(request, 'upload_multiple.html')
Pagination
Manual Pagination
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def article_list(request):
articles = Article.objects.filter(published=True)
paginator = Paginator(articles, 10) # 10 articles per page
page = request.GET.get('page')
try:
articles = paginator.page(page)
except PageNotAnInteger:
articles = paginator.page(1)
except EmptyPage:
articles = paginator.page(paginator.num_pages)
return render(request, 'article_list.html', {'articles': articles})
Class-Based View Pagination
class ArticleListView(ListView):
model = Article
template_name = 'article_list.html'
context_object_name = 'articles'
paginate_by = 10
def get_queryset(self):
return Article.objects.filter(published=True)
Search and Filtering
Search Views
from django.db.models import Q
def search_articles(request):
query = request.GET.get('q')
articles = Article.objects.filter(published=True)
if query:
articles = articles.filter(
Q(title__icontains=query) |
Q(content__icontains=query) |
Q(author__username__icontains=query)
)
return render(request, 'search_results.html', {
'articles': articles,
'query': query
})
def filter_articles(request):
articles = Article.objects.filter(published=True)
category = request.GET.get('category')
if category:
articles = articles.filter(category__slug=category)
date_from = request.GET.get('date_from')
if date_from:
articles = articles.filter(created_at__gte=date_from)
date_to = request.GET.get('date_to')
if date_to:
articles = articles.filter(created_at__lte=date_to)
return render(request, 'filtered_articles.html', {
'articles': articles,
'categories': Category.objects.all()
})
Error Handling
Custom Error Views
def custom_404(request, exception):
return render(request, 'errors/404.html', status=404)
def custom_500(request):
return render(request, 'errors/500.html', status=500)
def custom_403(request, exception):
return render(request, 'errors/403.html', status=403)
def custom_400(request, exception):
return render(request, 'errors/400.html', status=400)
Error Handling in Views
from django.core.exceptions import ValidationError
from django.db import IntegrityError
def create_article(request):
if request.method == 'POST':
try:
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save(commit=False)
article.author = request.user
article.save()
return redirect('article_detail', slug=article.slug)
except ValidationError as e:
messages.error(request, f'Validation error: {e}')
except IntegrityError as e:
messages.error(request, 'An article with this title already exists.')
except Exception as e:
messages.error(request, f'An error occurred: {e}')
else:
form = ArticleForm()
return render(request, 'create_article.html', {'form': form})
AJAX Views
AJAX Response Views
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def ajax_create_comment(request):
if request.method == 'POST':
article_id = request.POST.get('article_id')
content = request.POST.get('content')
try:
article = Article.objects.get(pk=article_id)
comment = Comment.objects.create(
article=article,
content=content,
author=request.user
)
return JsonResponse({
'success': True,
'comment': {
'id': comment.id,
'content': comment.content,
'author': comment.author.username,
'created_at': comment.created_at.isoformat(),
}
})
except Article.DoesNotExist:
return JsonResponse({
'success': False,
'error': 'Article not found'
})
return JsonResponse({'success': False, 'error': 'Invalid request'})
def ajax_search(request):
query = request.GET.get('q', '')
if query:
articles = Article.objects.filter(
title__icontains=query,
published=True
)[:10]
results = []
for article in articles:
results.append({
'id': article.id,
'title': article.title,
'url': article.get_absolute_url(),
})
return JsonResponse({'results': results})
return JsonResponse({'results': []})
Middleware Views
Custom Middleware
class CustomMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Process request
response = self.get_response(request)
# Process response
return response
def process_view(self, request, view_func, view_args, view_kwargs):
# Called before view
pass
def process_exception(self, request, exception):
# Called if view raises exception
pass
Testing Views
View Testing
from django.test import TestCase, Client
from django.contrib.auth.models import User
from django.urls import reverse
class ArticleViewTest(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.article = Article.objects.create(
title='Test Article',
content='Test content',
author=self.user,
published=True
)
def test_article_list_view(self):
response = self.client.get(reverse('article_list'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Test Article')
def test_article_detail_view(self):
response = self.client.get(
reverse('article_detail', kwargs={'slug': self.article.slug})
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, self.article.title)
def test_create_article_requires_login(self):
response = self.client.get(reverse('article_create'))
self.assertRedirects(response, '/login/?next=/articles/create/')
def test_create_article_authenticated(self):
self.client.login(username='testuser', password='testpass123')
response = self.client.post(reverse('article_create'), {
'title': 'New Article',
'content': 'New content',
})
self.assertEqual(response.status_code, 302)
self.assertTrue(Article.objects.filter(title='New Article').exists())