Build REST API for E-commerce App0%

Build REST API for E-commerce App

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

DRF Capstone: E-Commerce API Data Model and Architecture

In this capstone series, we design and build a production-grade, multi-model E-Commerce REST API. We begin by architecting the relational database schema, model relationships, managers, and data flow.


1. System Architecture & Entity Relationships

An enterprise E-Commerce API requires strict relational integrity across products, categories, shopping carts, orders, and user profiles:

Output
[ User ] (Django Auth)
│
├── (1:1) ── [ CustomerProfile ] (phone, address)
│
├── (1:N) ── [ Order ] (status, created_at, total)
│ │
│ └── (1:N) ── [ OrderItem ] (product, unit_price, quantity)
│ │
└── (1:1) ── [ Cart ] │
│ ▼
└── (1:N) ── [ CartItem ] ── (N:1) ── [ Product ] ── (N:1) ── [ Category ]

2. Model Definitions: Products & Categories

In store/models.py:

Python
import uuid
from django.db import models
from django.conf import settings
from django.core.validators import MinValueValidator
 
class Category(models.Model):
name = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=120, unique=True)
description = models.TextField(blank=True)
 
class Meta:
verbose_name_plural = 'Categories'
ordering = ['name']
 
def __str__(self):
return self.name
 
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.PROTECT, related_name='products')
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=220, unique=True)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(0.01)])
inventory = models.PositiveIntegerField()
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
 
class Meta:
ordering = ['-created_at']
 
def __str__(self):
return self.title

3. Carts, Orders, and Items

Python
class Cart(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
 
def __str__(self):
return str(self.id)
 
class CartItem(models.Model):
cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name='items')
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveSmallIntegerField(validators=[MinValueValidator(1)])
 
class Meta:
unique_together = [['cart', 'product']]
 
class Order(models.Model):
STATUS_PENDING = 'P'
STATUS_COMPLETE = 'C'
STATUS_FAILED = 'F'
STATUS_CHOICES = [
(STATUS_PENDING, 'Pending'),
(STATUS_COMPLETE, 'Complete'),
(STATUS_FAILED, 'Failed'),
]
 
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
customer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name='orders')
placed_at = models.DateTimeField(auto_now_add=True)
payment_status = models.CharField(max_length=1, choices=STATUS_CHOICES, default=STATUS_PENDING)
shipping_address = models.CharField(max_length=255)
 
class Meta:
ordering = ['-placed_at']
 
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.PROTECT, related_name='items')
product = models.ForeignKey(Product, on_delete=models.PROTECT)
unit_price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.PositiveSmallIntegerField()

Interactive Quiz

Practice Quiz

Question 1
Why is on_delete=models.PROTECT used for category on the Product model instead of CASCADE?
Question 2
Why is a UUID primary key preferable to an auto-incrementing integer ID for Cart and Order models?
Question 3
Why does OrderItem store a separate unit_price field instead of referencing product.price dynamically?
Question 4
What constraint does unique_together = [['cart', 'product']] enforce on CartItem?
Question 5
What does MinValueValidator(1) enforce on CartItem.quantity?
Next Lesson

CRUD, Authentication, Filtering, Pagination

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

Practice Quiz

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

PrevNext