How to Build an E-commerce Platform Using Python? A developer provides a practical guide to building an e-commerce platform with Python, recommending Django as the default framework for its built-in admin, ORM, and migrations, while noting that Flask suits thin API layers and Saleor or Oscar are worth evaluating for standard commerce needs. The guide emphasizes framework selection with real reasoning and warns against building custom solutions when hosted options like Shopify suffice. Every Python e-commerce article I've read follows the same structure: list the frameworks, list the features, add a conclusion that says "Python is great for e-commerce." Technically accurate. Not very useful if you're actually building something. This is a more practical take. I want to cover the decisions that actually matter — framework selection with real reasoning, the features that are harder than they look, code patterns worth knowing, and the architectural traps that are much easier to avoid early than fix later. Before any framework discussion — if your e-commerce requirements are standard product catalog, cart, checkout, Stripe payments, order management , consider whether you need to build at all. Shopify, WooCommerce, or even Saleor's hosted offering might get you to market faster with less ongoing maintenance burden. Custom Python makes sense when: If you can't articulate why Shopify doesn't work for you, you might be solving the wrong problem. Django is the right default. Not because it's the best in every dimension, but because it solves the right problems out of the box for commerce: Django model for a basic product — you get admin, ORM queries, migrations for free from django.db import models class Product models.Model : name = models.CharField max length=255 slug = models.SlugField unique=True price = models.DecimalField max digits=10, decimal places=2 stock = models.PositiveIntegerField default=0 is active = models.BooleanField default=True created at = models.DateTimeField auto now add=True class Meta: ordering = '-created at' def is in stock self : return self.stock 0 This model is immediately queryable, immediately visible in admin, and immediately migratable. That's Django's value proposition. Flask is a deliberate minimalism choice, not a default. You get routing and request handling. Everything else — ORM, auth, form validation, caching, admin — is a library you add yourself. python Flask requires you to wire everything manually from flask import Flask, request, jsonify from flask sqlalchemy import SQLAlchemy from flask login import LoginManager app = Flask name db = SQLAlchemy app login manager = LoginManager app You're assembling pieces; Django ships them assembled Flask makes sense when you're building a thin API layer over existing services, or when your "e-commerce platform" is really a specialized checkout flow sitting on top of proprietary backend logic. In those cases, you don't want Django's opinions — you want to compose your own. Saleor is a Django-based, GraphQL-first e-commerce framework. It's genuinely production-ready and ships with payment processing, multi-currency support, a product catalog, and a React-based dashboard. The trade-off: you're inheriting Saleor's data model. When your requirements fit, it's a significant head start. When they don't, you're working against the framework rather than with it. Worth evaluating if your requirements are within the standard commerce domain and you want to move fast. Oscar is another Django e-commerce framework, more flexible than Saleor in terms of data model customization. It's designed to be forked and extended — the "fork to customize" model rather than "override to customize." Oscar setup pip install django-oscar Fork the app you want to customize python manage.py oscar fork app catalogue myshop/ Oscar's product abstraction product classes, attributes, variants handles a wide range of product structures without requiring schema changes. Good for complex catalogs. Straightforward for simple catalogs. Gets complicated fast when you have: Oscar's product model handles variants well out of the box. If you're on raw Django, design your product schema carefully before you have real data — migrating product models with inventory is painful. Django's auth system is solid. For commerce specifically, layer on: settings.py — minimum security baseline for an e-commerce platform AUTH PASSWORD VALIDATORS = {'NAME': 'django.contrib.auth.password validation.MinimumLengthValidator', 'OPTIONS': {'min length': 10}}, {'NAME': 'django.contrib.auth.password validation.CommonPasswordValidator'}, SESSION COOKIE SECURE = True HTTPS only CSRF COOKIE SECURE = True SECURE HSSL REDIRECT = True X FRAME OPTIONS = 'DENY' Two-factor auth for admin access django-two-factor-auth , rate limiting on login endpoints, and proper PII handling for GDPR/CCPA compliance are all necessary additions. More complex than it looks. Cart state questions you'll face: If your checkout is reasonably standard, start with Saleor's or Oscar's checkout pipeline. These edge cases have already been thought through. Stripe is the right default for new builds. The Python SDK is well-maintained: python import stripe stripe.api key = settings.STRIPE SECRET KEY def create payment intent amount cents, currency, metadata : return stripe.PaymentIntent.create amount=amount cents, currency=currency, metadata=metadata, automatic payment methods={"enabled": True}, The thing to get right isn't which gateway — it's your payment state model. Payment flows have more edge cases than they appear failed charges, partial refunds, disputes, webhooks arriving out of order . Model payment state explicitly rather than inferring it from webhook history. The operational backbone. Beyond the basic status lifecycle, you need returns and refunds, order editing, and fulfillment integration. Django's ORM makes the data modeling clean: class Order models.Model : class Status models.TextChoices : PENDING = 'pending', 'Pending' CONFIRMED = 'confirmed', 'Confirmed' PROCESSING = 'processing', 'Processing' SHIPPED = 'shipped', 'Shipped' DELIVERED = 'delivered', 'Delivered' CANCELLED = 'cancelled', 'Cancelled' REFUNDED = 'refunded', 'Refunded' user = models.ForeignKey User, on delete=models.PROTECT status = models.CharField max length=20, choices=Status.choices, default=Status.PENDING total = models.DecimalField max digits=10, decimal places=2 created at = models.DateTimeField auto now add=True updated at = models.DateTimeField auto now=True Django ORM handles simple filtering. For anything beyond that, integrate a real search engine early: python Elasticsearch via elasticsearch-dsl-py from elasticsearch dsl import Document, Text, Keyword, Float class ProductIndex Document : name = Text analyzer='english' category = Keyword price = Float class Index: name = 'products' Typesense is a lighter alternative with good Python support and significantly easier self-hosting. Don't add this as an afterthought — retrofitting search infrastructure into an existing data pipeline is harder than building it in from the start. This one isn't in most feature lists but it should be. Email sends, webhook processing, inventory updates, search index updates, report generation — none of these should run synchronously in a request cycle: python Celery task for order confirmation email from celery import shared task from django.core.mail import send mail @shared task def send order confirmation order id : order = Order.objects.get id=order id send mail subject=f'Order {order.id} confirmed', message=render confirmation email order , from email=settings.DEFAULT FROM EMAIL, recipient list= order.user.email , Celery + Redis is the standard setup. Get it in early, because adding it after the fact means touching every place in your codebase that currently runs slow operations synchronously. Django is genuinely good here — clean URL routing, easy meta tag control, built-in sitemap generation. django-meta handles most of the boilerplate: SEO-friendly URL structure urlpatterns = path 'shop/