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:
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.
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)
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."
pip install django-oscar
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:
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:
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:
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:
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:
urlpatterns = [
path('shop/<slug:category_slug>/<slug:product_slug>/', views.product_detail, name='product-detail'),
]
For product variants, handle canonical URLs carefully to avoid duplicate content penalties.
Choose your frontend architecture before writing backend code. Django templates (server-rendered) vs. Django REST Framework + React frontend changes how you structure URLs, how you handle authentication (session-based vs JWT), and how you deliver pages to mobile. Either works — but mixing approaches mid-project is expensive.
Model your product schema for where you're going, not where you are. If there's any chance you'll have variants, bundles, or multiple product types, build that flexibility in. Oscar's product abstraction is worth studying even if you don't use Oscar.
Async tasks from day one. Celery + Redis. Don't postpone this.
Set up search infrastructure early. Elasticsearch or Typesense. Don't bolt it on later.
| Situation | Recommendation |
|---|---|
| Standard commerce, need to move fast | Saleor |
| Complex catalog, need flexibility | Oscar |
| Standard commerce, greenfield, full control | Django |
| Thin API layer over existing services | Flask |
| Non-standard requirements everywhere | Django from scratch |
At Innostax, we've shipped Python e-commerce platforms at various scales. If you're working through framework selection or architecture for a commerce build, reach out here — happy to think it through.
Originally published on the Innostax Engineering Blog.