Skip to main content

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())