1. What are Generic API Views?
Generic API Views are pre-built class-based views that handle common API patterns, so you don’t have to write the same code repeatedly.
from rest_framework import generics2. The Main Generic Views
ListAPIView - Read-Only List
from rest_framework import generics
from .models import Product
from .serializers import ProductSerializer
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# GET /api/products/ - returns list of all productsRetrieveAPIView - Read-Only Single Object
class ProductDetail(generics.RetrieveAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# GET /api/products/1/ - returns single productListCreateAPIView - List + Create
class ProductListCreate(generics.ListCreateAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# GET /api/products/ - list all
# POST /api/products/ - create newRetrieveUpdateAPIView - Read + Update
class ProductRetrieveUpdate(generics.RetrieveUpdateAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# GET /api/products/1/ - get single
# PUT /api/products/1/ - update full
# PATCH /api/products/1/ - update partialRetrieveDestroyAPIView - Read + Delete
class ProductRetrieveDestroy(generics.RetrieveDestroyAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# GET /api/products/1/ - get single
# DELETE /api/products/1/ - deleteRetrieveUpdateDestroyAPIView - Full CRUD on Single Object
class ProductDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# GET /api/products/1/ - read
# PUT /api/products/1/ - update full
# PATCH /api/products/1/ - update partial
# DELETE /api/products/1/ - deleteCreateAPIView - Create Only
class ProductCreate(generics.CreateAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# POST /api/products/ - create new only3. Core Components You MUST Define
Minimum Required
class ProductList(generics.ListAPIView):
queryset = Product.objects.all() # Required
serializer_class = ProductSerializer # RequiredOptional But Useful
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
permission_classes = [IsAuthenticated] # Who can access
filter_backends = [SearchFilter] # How to filter
search_fields = ['name', 'description'] # What to search
pagination_class = PageNumberPagination # Pagination
ordering_fields = ['price', 'name'] # Sorting fields4. Customizing Behavior
Override get_queryset() - Dynamic Filtering
class ActiveProducts(generics.ListAPIView):
serializer_class = ProductSerializer
def get_queryset(self):
# Only return active products
return Product.objects.filter(is_active=True)Override get_queryset() with URL Parameters
class CategoryProducts(generics.ListAPIView):
serializer_class = ProductSerializer
def get_queryset(self):
category_id = self.kwargs['category_id'] # From URL
return Product.objects.filter(category_id=category_id, is_active=True)URL for above
path('categories/<int:category_id>/products/', CategoryProducts.as_view())Override perform_create() - Custom Creation Logic
class ProductCreate(generics.CreateAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
def perform_create(self, serializer):
# Automatically set the creator to current user
serializer.save(created_by=self.request.user)5. Real-World Examples
Example 1: User Profile
class UserProfile(generics.RetrieveUpdateAPIView):
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
def get_object(self):
# Always return the current user's profile
return self.request.userExample 2: Searchable Product List
from rest_framework import filters
class ProductSearch(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = ['name', 'description', 'category__name']
ordering_fields = ['price', 'created_at', 'name']
ordering = ['-created_at'] # Default orderingExample 3: Category-specific Products
class CategoryProducts(generics.ListAPIView):
serializer_class = ProductSerializer
def get_queryset(self):
category_slug = self.kwargs['slug']
return Product.objects.filter(
category__slug=category_slug,
is_active=True
).select_related('category')6. Handling URL Parameters
Path Parameters (URL kwargs)
# urls.py
path('products/<int:pk>/', ProductDetail.as_view())
# views.py
class ProductDetail(generics.RetrieveAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# pk is automatically available in self.kwargs['pk']Query Parameters (GET params)
class ProductList(generics.ListAPIView):
serializer_class = ProductSerializer
def get_queryset(self):
queryset = Product.objects.all()
# Get query parameters
category = self.request.query_params.get('category')
min_price = self.request.query_params.get('min_price')
if category:
queryset = queryset.filter(category__name=category)
if min_price:
queryset = queryset.filter(price__gte=min_price)
return queryset
# Usage: /api/products/?category=electronics&min_price=1007. Permissions and Authentication
from rest_framework import generics, permissions
class ProductCreate(generics.CreateAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
permission_classes = [permissions.IsAuthenticated]
# or [permissions.IsAdminUser]
# or [permissions.IsAuthenticatedOrReadOnly]
# or [permissions.AllowAny] - default
class UserProducts(generics.ListAPIView):
serializer_class = ProductSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
# Only show current user's products
return Product.objects.filter(owner=self.request.user)8. Response Customization
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs)
# Customize the response
custom_data = {
'status': 'success',
'count': len(response.data),
'products': response.data
}
return Response(custom_data)9. Error Handling
class ProductDetail(generics.RetrieveAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
def get_object(self):
try:
return super().get_object()
except Product.DoesNotExist:
raise NotFound("Product not found")10. Complete Practical Example
Models
from django.db import models
class BlogPost(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_published = models.BooleanField(default=False)
author = models.ForeignKey('User', on_delete=models.CASCADE)Serializer
from rest_framework import serializers
from .models import BlogPost
class BlogPostSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPost
fields = ['id', 'title', 'content', 'created_at', 'updated_at', 'is_published', 'author']
read_only_fields = ['id', 'created_at', 'updated_at', 'author']Views
from rest_framework import generics, permissions
from .models import BlogPost
from .serializers import BlogPostSerializer
class BlogPostListCreate(generics.ListCreateAPIView):
serializer_class = BlogPostSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get_queryset(self):
# Show all published posts to anyone
# Show user's own drafts only to them
user = self.request.user
if user.is_authenticated:
return BlogPost.objects.filter(
models.Q(is_published=True) |
models.Q(author=user, is_published=False)
)
return BlogPost.objects.filter(is_published=True)
def perform_create(self, serializer):
serializer.save(author=self.request.user)
class BlogPostDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = BlogPostSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get_queryset(self):
# Same filtering logic as above
user = self.request.user
if user.is_authenticated:
return BlogPost.objects.filter(
models.Q(is_published=True) |
models.Q(author=user, is_published=False)
)
return BlogPost.objects.filter(is_published=True)URLs
from django.urls import path
from .views import BlogPostListCreate, BlogPostDetail
urlpatterns = [
path('posts/', BlogPostListCreate.as_view(), name='post-list-create'),
path('posts/<int:pk>/', BlogPostDetail.as_view(), name='post-detail'),
]Quick Reference - Which Generic View to Use?
| Use Case | Generic View |
|---|---|
| List objects only | ListAPIView |
| Get single object | RetrieveAPIView |
| List + Create | ListCreateAPIView |
| Get + Update | RetrieveUpdateAPIView |
| Get + Delete | RetrieveDestroyAPIView |
| Full CRUD on single | RetrieveUpdateDestroyAPIView |
| Create only | CreateAPIView |
| Update only | UpdateAPIView |
Key Takeaways
- Less Code: Generic views handle common patterns automatically
- Consistent: Follow REST conventions out of the box
- Customizable: Override methods for specific behavior
- Secure: Built-in permission and authentication support
- Maintainable: Clean separation of concerns