CRUD, Authentication, Filtering, Pagination0%

CRUD, Authentication, Filtering, Pagination

Beginner12 min read•Updated: Jul 11, 2026
Study MaterialsCheatsheet

DRF Capstone: Orders, Cart Checkout, and JWT Workflows

Building on our data models, we now implement the business logic: cart serializers, dynamic calculations, checkout transactions with atomic inventory decrements, and secure JWT authentication.


1. Serializers for Products, Carts, and Items

In store/serializers.py:

Python
from rest_framework import serializers
from decimal import Decimal
from store.models import Category, Product, Cart, CartItem, Order, OrderItem
 
class ProductSerializer(serializers.ModelSerializer):
price_with_tax = serializers.SerializerMethodField()
 
class Meta:
model = Product
fields = ['id', 'title', 'slug', 'description', 'price', 'price_with_tax', 'inventory', 'category']
 
def get_price_with_tax(self, product):
return round(product.price * Decimal(1.10), 2)
 
class CartItemSerializer(serializers.ModelSerializer):
total_price = serializers.SerializerMethodField()
product = ProductSerializer(read_only=True)
 
class Meta:
model = CartItem
fields = ['id', 'product', 'quantity', 'total_price']
 
def get_total_price(self, cart_item):
return cart_item.quantity * cart_item.product.price
 
class AddCartItemSerializer(serializers.ModelSerializer):
product_id = serializers.IntegerField()
 
class Meta:
model = CartItem
fields = ['id', 'product_id', 'quantity']
 
def validate_product_id(self, value):
if not Product.objects.filter(pk=value).exists():
raise serializers.ValidationError("No product found with given ID.")
return value
 
def save(self, **kwargs):
cart_id = self.context['cart_id']
product_id = self.validated_data['product_id']
quantity = self.validated_data['quantity']
 
try:
cart_item = CartItem.objects.get(cart_id=cart_id, product_id=product_id)
cart_item.quantity += quantity
cart_item.save()
self.instance = cart_item
except CartItem.DoesNotExist:
self.instance = CartItem.objects.create(cart_id=cart_id, **self.validated_data)
return self.instance

2. Atomic Checkout with transaction.atomic

When converting a cart to an order:

  1. 1
    Verify cart is not empty.
  2. 2
    Check inventory availability.
  3. 3
    Decrement product stock.
  4. 4
    Create order & order items.
  5. 5
    Delete the cart.
Python
from django.db import transaction
from rest_framework.exceptions import ValidationError
 
class CreateOrderSerializer(serializers.Serializer):
cart_id = serializers.UUIDField()
shipping_address = serializers.CharField(max_length=255)
 
def validate_cart_id(self, cart_id):
if not Cart.objects.filter(pk=cart_id).exists():
raise ValidationError("Invalid cart ID.")
if CartItem.objects.filter(cart_id=cart_id).count() == 0:
raise ValidationError("The shopping cart is empty.")
return cart_id
 
def save(self, **kwargs):
with transaction.atomic():
cart_id = self.validated_data['cart_id']
user = self.context['user']
shipping_address = self.validated_data['shipping_address']
 
# Create Order
order = Order.objects.create(
customer=user,
shipping_address=shipping_address
)
 
cart_items = CartItem.objects.select_related('product').filter(cart_id=cart_id)
order_items = []
 
for item in cart_items:
product = item.product
if product.inventory < item.quantity:
raise ValidationError(f"Insufficient stock for {product.title}. Available: {product.inventory}")
# Deduct inventory
product.inventory -= item.quantity
product.save()
 
order_items.append(
OrderItem(
order=order,
product=product,
unit_price=product.price,
quantity=item.quantity
)
)
 
OrderItem.objects.bulk_create(order_items)
# Flush cart
Cart.objects.filter(pk=cart_id).delete()
 
return order

Interactive Quiz

Practice Quiz

Question 1
Why is transaction.atomic() indispensable during the order creation workflow?
Question 2
Why is OrderItem.objects.bulk_create(order_items) preferred over calling .save() in a loop?
Question 3
What happens in AddCartItemSerializer.save() if a customer adds a product that already exists in their cart?
Question 4
Why is select_related('product') used when querying CartItem.objects.filter(cart_id=cart_id)?
Question 5
Where should user = self.context['user'] be supplied to the serializer?
Next Lesson

Test & Document API

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Related Lessons

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext