Create RESTful APIs with Django REST Framework, serializers, viewsets, and API endpoints
REST APIs are the backbone of modern web applications, enabling communication between frontend applications, mobile apps, and third-party services. Django REST Framework (DRF) is the most powerful toolkit for building APIs in Python.
Why Django REST Framework?
What You'll Build:
A complete RESTful API for an e-commerce platform with product management, user authentication, shopping cart functionality, and comprehensive API documentation.
Enterprise API Features:
REST (Representational State Transfer) is an architectural style for web services:
Core Principles:
HTTP Methods:
Status Codes:
Resource Design:
1GET /api/products/ # List all products
2POST /api/products/ # Create new product
3GET /api/products/123/ # Get specific product
4PUT /api/products/123/ # Update entire product
5PATCH /api/products/123/ # Partial update
6DELETE /api/products/123/ # Delete productSerializers convert Django model instances to JSON and validate incoming data:
Serializer Types:
Basic Serializer:
1from rest_framework import serializers
2from .models import Product
3
4class ProductSerializer(serializers.ModelSerializer):
5 class Meta:
6 model = Product
7 fields = ['id', 'name', 'description', 'price', 'category']
8 read_only_fields = ['id', 'created_at']
9
10 def validate_price(self, value):
11 if value <= 0:
12 raise serializers.ValidationError('Price must be positive')
13 return value
14
15 def create(self, validated_data):
16 return Product.objects.create(**validated_data)Advanced Features:
DRF provides powerful view classes for building APIs quickly:
ViewSet Types:
Generic Views:
ModelViewSet Example:
1from rest_framework import viewsets, permissions
2from rest_framework.decorators import action
3from rest_framework.response import Response
4
5class ProductViewSet(viewsets.ModelViewSet):
6 queryset = Product.objects.all()
7 serializer_class = ProductSerializer
8 permission_classes = [permissions.IsAuthenticated]
9
10 @action(detail=True, methods=['post'])
11 def add_to_cart(self, request, pk=None):
12 product = self.get_object()
13 # Add to cart logic
14 return Response({'status': 'added to cart'})
15
16 def get_queryset(self):
17 queryset = Product.objects.all()
18 category = self.request.query_params.get('category')
19 if category:
20 queryset = queryset.filter(category=category)
21 return querysetRouter Configuration:
1from rest_framework.routers import DefaultRouter
2router = DefaultRouter()
3router.register(r'products', ProductViewSet)
4urlpatterns = router.urlsBuild a production-ready e-commerce API with product management, user authentication, shopping cart, and order processing. Includes serializers, viewsets, permissions, and comprehensive documentation.
1# šļø COMPLETE E-COMMERCE REST API
2# Production-ready Django REST Framework implementation
3
4# models.py - E-commerce Models
5from django.db import models
6from django.contrib.auth.models import User
7from django.core.validators import MinValueValidator, MaxValueValidator
8
9class Category(models.Model):
10 """Product categories"""
11 name = models.CharField(max_length=100, unique=True)
12 slug = models.SlugField(max_length=100, unique=True)
13 description = models.TextField(blank=True)
14 image = models.ImageField(upload_to='categories/', blank=True, null=True)
15 is_active = models.BooleanField(default=True)
16 created_at = models.DateTimeField(auto_now_add=True)
17
18 class Meta:
19 verbose_name_plural = "Categories"
20 ordering = ['name']
21
22 def __str__(self):
23 return self.name
24
25class Product(models.Model):
26 """Main product model"""
27 name = models.CharField(max_length=200)
28 slug = models.SlugField(max_length=200, unique=True)
29 description = models.TextField()
30 price = models.DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(0.01)])
31 category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products')
32 stock_quantity = models.PositiveIntegerField(default=0)
33 image = models.ImageField(upload_to='products/', blank=True, null=True)
34 is_active = models.BooleanField(default=True)
35 created_at = models.DateTimeField(auto_now_add=True)
36 updated_at = models.DateTimeField(auto_now=True)
37
38 class Meta:
39 ordering = ['-created_at']
40 indexes = [
41 models.Index(fields=['category', 'is_active']),
42 models.Index(fields=['price']),
43 ]
44
45 def __str__(self):
46 return self.name
47
48 @property
49 def is_in_stock(self):
50 return self.stock_quantity > 0
51
52class CartItem(models.Model):
53 """Shopping cart items"""
54 user = models.ForeignKey(User, on_delete=models.CASCADE)
55 product = models.ForeignKey(Product, on_delete=models.CASCADE)
56 quantity = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)])
57 created_at = models.DateTimeField(auto_now_add=True)
58
59 class Meta:
60 unique_together = ['user', 'product']
61
62 def __str__(self):
63 return f"{self.user.username} - {self.product.name} x{self.quantity}"
64
65 @property
66 def total_price(self):
67 return self.product.price * self.quantity
68
69class Order(models.Model):
70 """Customer orders"""
71 STATUS_CHOICES = [
72 ('pending', 'Pending'),
73 ('processing', 'Processing'),
74 ('shipped', 'Shipped'),
75 ('delivered', 'Delivered'),
76 ('cancelled', 'Cancelled'),
77 ]
78
79 user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders')
80 status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
81 total_amount = models.DecimalField(max_digits=10, decimal_places=2)
82 shipping_address = models.TextField()
83 created_at = models.DateTimeField(auto_now_add=True)
84 updated_at = models.DateTimeField(auto_now=True)
85
86 class Meta:
87 ordering = ['-created_at']
88
89 def __str__(self):
90 return f"Order #{self.id} - {self.user.username}"
91
92class OrderItem(models.Model):
93 """Items within an order"""
94 order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
95 product = models.ForeignKey(Product, on_delete=models.CASCADE)
96 quantity = models.PositiveIntegerField()
97 price = models.DecimalField(max_digits=10, decimal_places=2) # Price at time of order
98
99 def __str__(self):
100 return f"{self.product.name} x{self.quantity}"
101
102# serializers.py - API Serializers
103from rest_framework import serializers
104from .models import Category, Product, CartItem, Order, OrderItem
105
106class CategorySerializer(serializers.ModelSerializer):
107 """Category serializer with product count"""
108 product_count = serializers.SerializerMethodField()
109
110 class Meta:
111 model = Category
112 fields = ['id', 'name', 'slug', 'description', 'image', 'product_count', 'is_active']
113 read_only_fields = ['id']
114
115 def get_product_count(self, obj):
116 return obj.products.filter(is_active=True).count()
117
118class ProductListSerializer(serializers.ModelSerializer):
119 """Lightweight product serializer for listing"""
120 category_name = serializers.CharField(source='category.name', read_only=True)
121
122 class Meta:
123 model = Product
124 fields = ['id', 'name', 'slug', 'price', 'category_name', 'image', 'is_in_stock']
125
126class ProductDetailSerializer(serializers.ModelSerializer):
127 """Detailed product serializer"""
128 category = CategorySerializer(read_only=True)
129 category_id = serializers.IntegerField(write_only=True)
130
131 class Meta:
132 model = Product
133 fields = ['id', 'name', 'slug', 'description', 'price', 'category', 'category_id',
134 'stock_quantity', 'image', 'is_active', 'is_in_stock', 'created_at']
135 read_only_fields = ['id', 'created_at']
136
137 def validate_price(self, value):
138 if value <= 0:
139 raise serializers.ValidationError('Price must be greater than zero.')
140 return value
141
142 def validate_stock_quantity(self, value):
143 if value < 0:
144 raise serializers.ValidationError('Stock quantity cannot be negative.')
145 return value
146
147class CartItemSerializer(serializers.ModelSerializer):
148 """Cart item serializer with product details"""
149 product = ProductListSerializer(read_only=True)
150 product_id = serializers.IntegerField(write_only=True)
151 total_price = serializers.DecimalField(max_digits=10, decimal_places=2, read_only=True)
152
153 class Meta:
154 model = CartItem
155 fields = ['id', 'product', 'product_id', 'quantity', 'total_price', 'created_at']
156 read_only_fields = ['id', 'created_at']
157
158 def validate_quantity(self, value):
159 if value <= 0:
160 raise serializers.ValidationError('Quantity must be greater than zero.')
161 return value
162
163 def validate(self, data):
164 """Validate stock availability"""
165 product_id = data.get('product_id')
166 quantity = data.get('quantity')
167
168 if product_id:
169 try:
170 product = Product.objects.get(id=product_id)
171 if quantity > product.stock_quantity:
172 raise serializers.ValidationError(
173 f'Only {product.stock_quantity} items available in stock.'
174 )
175 except Product.DoesNotExist:
176 raise serializers.ValidationError('Product does not exist.')
177
178 return data
179
180class OrderItemSerializer(serializers.ModelSerializer):
181 """Order item serializer"""
182 product_name = serializers.CharField(source='product.name', read_only=True)
183
184 class Meta:
185 model = OrderItem
186 fields = ['id', 'product', 'product_name', 'quantity', 'price']
187
188class OrderSerializer(serializers.ModelSerializer):
189 """Order serializer with items"""
190 items = OrderItemSerializer(many=True, read_only=True)
191 user_email = serializers.CharField(source='user.email', read_only=True)
192
193 class Meta:
194 model = Order
195 fields = ['id', 'user', 'user_email', 'status', 'total_amount',
196 'shipping_address', 'items', 'created_at', 'updated_at']
197 read_only_fields = ['id', 'user', 'created_at', 'updated_at']
198
199# views.py - API Views
200from rest_framework import viewsets, permissions, status
201from rest_framework.decorators import action
202from rest_framework.response import Response
203from rest_framework.filters import SearchFilter, OrderingFilter
204from django_filters.rest_framework import DjangoFilterBackend
205from django.db.models import Q
206
207class CategoryViewSet(viewsets.ModelViewSet):
208 """Category CRUD operations"""
209 queryset = Category.objects.filter(is_active=True)
210 serializer_class = CategorySerializer
211 permission_classes = [permissions.IsAuthenticatedOrReadOnly]
212 lookup_field = 'slug'
213
214 def get_permissions(self):
215 """Different permissions for different actions"""
216 if self.action in ['create', 'update', 'partial_update', 'destroy']:
217 permission_classes = [permissions.IsAdminUser]
218 else:
219 permission_classes = [permissions.AllowAny]
220 return [permission() for permission in permission_classes]
221
222class ProductViewSet(viewsets.ModelViewSet):
223 """Product CRUD with advanced filtering"""
224 queryset = Product.objects.filter(is_active=True).select_related('category')
225 permission_classes = [permissions.IsAuthenticatedOrReadOnly]
226 filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
227 filterset_fields = ['category', 'price']
228 search_fields = ['name', 'description']
229 ordering_fields = ['price', 'created_at', 'name']
230 ordering = ['-created_at']
231
232 def get_serializer_class(self):
233 """Use different serializers for list and detail views"""
234 if self.action == 'list':
235 return ProductListSerializer
236 return ProductDetailSerializer
237
238 def get_permissions(self):
239 """Different permissions for different actions"""
240 if self.action in ['create', 'update', 'partial_update', 'destroy']:
241 permission_classes = [permissions.IsAdminUser]
242 else:
243 permission_classes = [permissions.AllowAny]
244 return [permission() for permission in permission_classes]
245
246 @action(detail=False, methods=['get'])
247 def featured(self, request):
248 """Get featured products"""
249 featured_products = self.get_queryset()[:6]
250 serializer = self.get_serializer(featured_products, many=True)
251 return Response(serializer.data)
252
253 @action(detail=True, methods=['get'])
254 def related(self, request, pk=None):
255 """Get related products in same category"""
256 product = self.get_object()
257 related = Product.objects.filter(
258 category=product.category,
259 is_active=True
260 ).exclude(id=product.id)[:4]
261 serializer = ProductListSerializer(related, many=True)
262 return Response(serializer.data)
263
264class CartViewSet(viewsets.ModelViewSet):
265 """Shopping cart management"""
266 serializer_class = CartItemSerializer
267 permission_classes = [permissions.IsAuthenticated]
268
269 def get_queryset(self):
270 """Return cart items for current user"""
271 return CartItem.objects.filter(user=self.request.user).select_related('product')
272
273 def perform_create(self, serializer):
274 """Assign cart item to current user"""
275 serializer.save(user=self.request.user)
276
277 @action(detail=False, methods=['get'])
278 def total(self, request):
279 """Get cart total"""
280 cart_items = self.get_queryset()
281 total = sum(item.total_price for item in cart_items)
282 return Response({
283 'total_amount': total,
284 'item_count': cart_items.count()
285 })
286
287 @action(detail=False, methods=['delete'])
288 def clear(self, request):
289 """Clear entire cart"""
290 deleted_count = self.get_queryset().delete()[0]
291 return Response({
292 'message': f'Removed {deleted_count} items from cart'
293 })
294
295class OrderViewSet(viewsets.ModelViewSet):
296 """Order management"""
297 serializer_class = OrderSerializer
298 permission_classes = [permissions.IsAuthenticated]
299
300 def get_queryset(self):
301 """Return orders for current user or all for admin"""
302 if self.request.user.is_staff:
303 return Order.objects.all().prefetch_related('items__product')
304 return Order.objects.filter(user=self.request.user).prefetch_related('items__product')
305
306 def perform_create(self, serializer):
307 """Create order from cart items"""
308 user = self.request.user
309 cart_items = CartItem.objects.filter(user=user)
310
311 if not cart_items.exists():
312 raise serializers.ValidationError('Cart is empty')
313
314 # Calculate total
315 total_amount = sum(item.total_price for item in cart_items)
316
317 # Create order
318 order = serializer.save(user=user, total_amount=total_amount)
319
320 # Create order items
321 for cart_item in cart_items:
322 OrderItem.objects.create(
323 order=order,
324 product=cart_item.product,
325 quantity=cart_item.quantity,
326 price=cart_item.product.price
327 )
328
329 # Clear cart
330 cart_items.delete()
331
332# urls.py - API URL Configuration
333from rest_framework.routers import DefaultRouter
334from django.urls import path, include
335
336router = DefaultRouter()
337router.register(r'categories', CategoryViewSet)
338router.register(r'products', ProductViewSet)
339router.register(r'cart', CartViewSet, basename='cart')
340router.register(r'orders', OrderViewSet, basename='order')
341
342urlpatterns = [
343 path('api/v1/', include(router.urls)),
344 path('api-auth/', include('rest_framework.urls')),
345]
346
347# Demonstration: API Testing
348print("šļø E-commerce REST API Complete!")
349print("=" * 60)
350
351print("\nš API Endpoints Available:")
352endpoints = [
353 ("GET", "/api/v1/categories/", "List all categories"),
354 ("POST", "/api/v1/categories/", "Create new category (admin only)"),
355 ("GET", "/api/v1/products/", "List products with filtering"),
356 ("POST", "/api/v1/products/", "Create new product (admin only)"),
357 ("GET", "/api/v1/products/featured/", "Get featured products"),
358 ("GET", "/api/v1/products/{id}/related/", "Get related products"),
359 ("GET", "/api/v1/cart/", "View cart items"),
360 ("POST", "/api/v1/cart/", "Add item to cart"),
361 ("GET", "/api/v1/cart/total/", "Get cart total"),
362 ("DELETE", "/api/v1/cart/clear/", "Clear entire cart"),
363 ("GET", "/api/v1/orders/", "List user orders"),
364 ("POST", "/api/v1/orders/", "Create order from cart"),
365]
366
367for method, url, description in endpoints:
368 print(f"{method:6} {url:35} - {description}")
369
370print("\nš API Features Demonstrated:")
371features = [
372 "ModelViewSet for complete CRUD operations",
373 "Custom serializers with validation",
374 "Permission classes for security",
375 "Search and filtering capabilities",
376 "Custom actions for additional endpoints",
377 "Different serializers for list/detail views",
378 "Nested relationships with related data",
379 "Performance optimization with select_related",
380 "Shopping cart functionality",
381 "Order processing workflow"
382]
383
384for i, feature in enumerate(features, 1):
385 print(f"{i:2d}. {feature}")
386
387print("\nš”ļø Security Features:")
388security = [
389 "Authentication required for sensitive operations",
390 "Permission classes for different user roles",
391 "Input validation in serializers",
392 "CSRF protection (built-in)",
393 "SQL injection prevention via ORM",
394 "Admin-only endpoints for management"
395]
396
397for i, feature in enumerate(security, 1):
398 print(f"{i:2d}. {feature}")
399
400print("\nš Advanced API Features:")
401advanced = [
402 "Filtering by category and price",
403 "Search across product name and description",
404 "Ordering by multiple fields",
405 "Pagination for large datasets",
406 "Related product recommendations",
407 "Cart total calculation",
408 "Stock quantity validation",
409 "Order creation from cart"
410]
411
412for i, feature in enumerate(advanced, 1):
413 print(f"{i:2d}. {feature}")
414
415print("\nšÆ Testing Examples:")
416print("1. GET /api/v1/products/?category=1&search=laptop&ordering=-price")
417print("2. POST /api/v1/cart/ {'product_id': 1, 'quantity': 2}")
418print("3. GET /api/v1/products/1/related/")
419print("4. POST /api/v1/orders/ {'shipping_address': '123 Main St'}")
420
421print("\nā
Production Ready Features:")
422print("⢠Comprehensive error handling")
423print("⢠Data validation and sanitization")
424print("⢠Performance optimized queries")
425print("⢠RESTful design principles")
426print("⢠Browsable API interface")
427print("⢠Automatic API documentation")š REST API Architecture Breakdown:
1. Models & Database Design:
Category: Product categorization with SEO-friendly slugsProduct: Main product model with pricing and inventoryCartItem: Shopping cart functionality with user associationOrder & OrderItem: Complete order processing workflow2. Serializers Architecture:
CategorySerializer: Includes computed product countProductListSerializer: Lightweight for listing performanceProductDetailSerializer: Complete product informationCartItemSerializer: Shopping cart with validationOrderSerializer: Order processing with nested items3. ViewSets & API Endpoints:
ModelViewSet: Complete CRUD operations for resources4. Authentication & Permissions:
IsAuthenticatedOrReadOnly: Public read, authenticated writeIsAdminUser: Administrative operations only5. Advanced API Features:
6. E-commerce Workflow:
7. Production Features:
This implementation demonstrates enterprise-grade API development with DRF!
Your progress is saved in this browser, on this device only.