GET /api/products/ - returns list of all products

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 generics

2. 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 products

RetrieveAPIView - Read-Only Single Object

class ProductDetail(generics.RetrieveAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    
# GET /api/products/1/ - returns single product

ListCreateAPIView - List + Create

class ProductListCreate(generics.ListCreateAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    
# GET /api/products/ - list all
# POST /api/products/ - create new

RetrieveUpdateAPIView - 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 partial

RetrieveDestroyAPIView - Read + Delete

class ProductRetrieveDestroy(generics.RetrieveDestroyAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    
# GET /api/products/1/ - get single
# DELETE /api/products/1/ - delete

RetrieveUpdateDestroyAPIView - 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/ - delete

CreateAPIView - Create Only

class ProductCreate(generics.CreateAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    
# POST /api/products/ - create new only

3. Core Components You MUST Define

Minimum Required

class ProductList(generics.ListAPIView):
    queryset = Product.objects.all()           # Required
    serializer_class = ProductSerializer       # Required

Optional 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 fields

4. 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.user

Example 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 ordering

Example 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=100

7. 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 CaseGeneric View
List objects onlyListAPIView
Get single objectRetrieveAPIView
List + CreateListCreateAPIView
Get + UpdateRetrieveUpdateAPIView
Get + DeleteRetrieveDestroyAPIView
Full CRUD on singleRetrieveUpdateDestroyAPIView
Create onlyCreateAPIView
Update onlyUpdateAPIView

Key Takeaways

  1. Less Code: Generic views handle common patterns automatically
  2. Consistent: Follow REST conventions out of the box
  3. Customizable: Override methods for specific behavior
  4. Secure: Built-in permission and authentication support
  5. Maintainable: Clean separation of concerns