Build a Python Shopping Cart with Flask and PostgreSQL
Build a Python Shopping Cart with Flask and PostgreSQL
Master Flask by building a complete e-commerce platform with Python. This tutorial teaches shopping cart implementation, Stripe payment integration, order management, and product catalog design. Perfect for Python developers building their first online store.
What You'll Build
- E-commerce platform with product catalog and categories
- Shopping cart using Flask sessions
- Stripe payment integration and checkout process
- Order management and tracking system
- Admin dashboard for inventory and sales
- Customer accounts with order history
Flask E-Commerce Concepts You'll Learn
- Shopping Cart: Session-based cart management in Flask
- Payment Processing: Stripe API integration for secure payments
- SQLAlchemy Models: Product, Order, and Customer relationships
- Flask Blueprints: Modular application structure for shop, cart, and admin
- Order Workflow: Complete purchase flow from cart to confirmation
- Flask-Mail: Email notifications for orders
- Admin Panel: Product and order management interface
Prerequisites
- Intermediate Python programming skills
- Basic Flask knowledge (or complete our blog/portfolio tutorials)
- Understanding of e-commerce concepts
- PostgreSQL or SQLite database
- Stripe developer account (free)
Time Commitment: 9-11 hours total. Comprehensive e-commerce project with AI-assisted prompts for every step.
Why Flask for E-Commerce?
Flask's lightweight architecture makes it perfect for custom e-commerce solutions. Unlike monolithic frameworks, Flask lets you integrate exactly the payment gateways, shipping APIs, and inventory systems your store needs. This flexibility means faster page loads and easier customizationβcrucial for online retail.
This tutorial teaches production-ready e-commerce patterns with proper security, payment handling, and order management. You'll build a portfolio-worthy project demonstrating advanced Python web development skills. Explore more about Flask for production applications.
Build a Python Shopping Cart with Flask and PostgreSQL
Harness the simplicity and flexibility of Python's Flask framework to build a lean, efficient e-commerce platform that's easy to understand and extend. Combined with PostgreSQL's rock-solid reliability and SQLAlchemy's intuitive ORM, this minimalist stack gives you complete control over your shopping experience without the overhead of larger frameworks. Perfect for developers who value Python's readability and want to create a custom e-commerce solution that grows with their business - from MVP to production-ready platform.
Flask E-Commerce Setup: Installation and Project Configuration
Initialize Flask project
Initialize a new Flask e-commerce project Create a project directory, set up a virtual environment with python -m venv venv, activate it (source venv/bin/activate on Unix or venvScriptsactivate on Windows) Install Flask with pip install Flask Create the project structure: app/ folder for application code with blueprints/ subdirectory, templates/ for Jinja2 templates (shop/, cart/, checkout/ subdirs), static/ for CSS/JS/images (with css/, js/, images/ subdirs), instance/ for config files Create app.py or app/__init__.py as the application factory Set up requirements.txt to track dependencies Initialize git repository with appropriate .gitignore for Python/Flask e-commerce projects (.env, venv/, instance/, *.pyc).
Configure VS Code with Python
Configure VS Code for Flask e-commerce development Install Python extension by Microsoft, Pylance for IntelliSense, Jinja template highlighting extension Create .vscode/settings.json with Python interpreter path pointing to venv, enable pylint or flake8 for linting with e-commerce specific rules, configure Black or autopep8 for formatting Set up launch.json for Flask debugging with FLASK_APP and FLASK_ENV variables Add .env file support with python-dotenv Configure editor to use 4-space indentation (PEP 8 standard) Install additional extensions: Python Test Explorer for pytest integration, SQLAlchemy extension for model support Add code snippets for common Flask e-commerce patterns (models, routes, forms).
Flask, Flask-SQLAlchemy, Stripe
Install essential Flask extensions for e-commerce functionality Run pip install Flask Flask-SQLAlchemy Flask-Migrate Flask-Login Flask-WTF for core functionality Add stripe for payment processing, email-validator for form validation Install Pillow for image processing, Flask-Uploads or werkzeug for file uploads Add python-slugify for URL-friendly product slugs Install Flask-Mail for order confirmation emails, Flask-Bcrypt for password hashing For development, install Flask-DebugToolbar Add gunicorn for production server Update requirements.txt with pip freeze > requirements.txt Configure each extension in your Flask app factory with app.config settings: SQLALCHEMY_DATABASE_URI, SECRET_KEY, STRIPE_PUBLIC_KEY, STRIPE_SECRET_KEY, MAIL_SERVER settings Initialize extensions with db.init_app(app), migrate.init_app(app, db), login_manager.init_app(app), etc.
Database and Environment Configuration for Flask
Configure PostgreSQL
Configure PostgreSQL database for Flask e-commerce app Install psycopg2-binary for PostgreSQL support For development, you can also use SQLite Create config.py with different configurations for development, testing, and production. Set SQLALCHEMY_DATABASE_URI for PostgreSQL: "postgresql://username:password@localhost/shopdb" or SQLite: "sqlite:///shop.db" for development. Set SQLALCHEMY_TRACK_MODIFICATIONS = False to suppress warnings Create the PostgreSQL database using psql command line or pgAdmin: CREATE DATABASE shopdb; Initialize Flask-Migrate with flask db init to create migrations folder Set up environment variables in .env file for sensitive credentials: DATABASE_URL, SECRET_KEY Configure database connection pooling for production Plan database schema: users, products, categories, orders, order_items, cart_items tables with proper relationships and indexes.
Create Product, Order, User models
Create comprehensive SQLAlchemy models in app/models.py Define User model with id, username, email, password_hash, created_at, role (customer/admin), relationship to orders and cart_items, password hashing methods (set_password, check_password) using Flask-Bcrypt Define Product model with id, name, slug, description, price, compare_price, sku, quantity, category_id, image_url, images (JSON array), is_active, is_featured, created_at, updated_at, relationship to Category (foreign key), OrderItems, reviews Add slug generation from name using python-slugify Create Category model with id, name, slug, parent_id (self-referential), description, relationship to products Build Order model with id, order_number, user_id, status (pending/processing/shipped/completed/cancelled), subtotal, tax, shipping, total, shipping_address (JSON), billing_address (JSON), payment_method, payment_status, payment_id, created_at, relationship to User and OrderItems Create OrderItem model with id, order_id, product_id, quantity, unit_price, total Add CartItem model for persistent cart: id, user_id, product_id, quantity, created_at Implement __repr__ methods, property methods for calculations, class methods for queries Add indexes on slug, sku, status, created_at for performance.
Set up Jinja2 templates
Set up comprehensive Jinja2 template structure in templates/ directory
Create base.html with HTML boilerplate, Bootstrap 5 or Tailwind CSS CDN links, navigation bar with logo, search bar, cart icon with item count badge (use session data), flash messages display block, content block with {% block content %}{% endblock %}, and footer with links and social icons
Build layouts for e-commerce: templates/shop/index.html (product grid listing), templates/shop/product.html (single product detail), templates/cart/index.html (cart page with items table), templates/checkout/index.html (checkout form)
Create reusable macros in templates/macros/product.html: product_card(product) for displaying product cards, pagination(pagination_obj) for pagination controls
Set up static folder structure: static/css/style.css for custom styles, static/js/cart.js for cart interactions, static/images/ for product images
Add CSS for responsive grid layouts, hover effects on product cards, form styling
Configure Flask to serve static files correctly
Use Jinja2 filters for formatting: {{ price|format_currency }}, {{ date|format_date }}
Implement template inheritance for consistent layout across all pages.Building E-Commerce Features: Core Functionality and Admin Panel
Organize into shop, cart, admin blueprints
Organize Flask e-commerce application using blueprints for better structure
Create app/blueprints/ directory with separate blueprints: app/blueprints/shop/ for public product pages (index, product detail), app/blueprints/cart/ for cart operations (view, add, update, remove), app/blueprints/checkout/ for checkout process (form, payment, confirmation), app/blueprints/auth/ for authentication (login, register, logout), app/blueprints/account/ for customer account (orders, profile), app/blueprints/admin/ for admin panel (dashboard, products, orders)
Each blueprint should have its own __init__.py, routes.py, forms.py
Register blueprints in app factory with url_prefix (e.g., /shop, /cart, /checkout, /admin)
Move routes from main app.py into appropriate blueprint route files
Use url_for with blueprint name: url_for("shop.product", id=1)
Set up blueprint-specific templates and static folders if needed. This structure makes the codebase more maintainable and scalable for growing e-commerce functionality.Build product listing and detail pages
Build comprehensive product catalog with listing and detail pages In shop blueprint routes.py, create index() route for product listing: query Product.query.filter_by(is_active=True), implement pagination with .paginate(page, per_page=12), add filtering by category with query parameters, sorting options (price, newest, name), search functionality using ilike() on name/description Render shop/index.html template with products, categories for sidebar filters Create product() route for detail page: query product by slug or id, handle 404 if not found, query related products in same category, render shop/product.html with product details, image gallery, add-to-cart form Build product detail template with product images (use carousel for multiple images), name, price (show compare_price if available), SKU, stock status (in stock/out of stock based on quantity), description, specifications, add-to-cart form with quantity selector, breadcrumbs navigation, related products section Add category pages: category() route that filters products by category slug Implement product search: search() route that queries products by name/description using LIKE or full-text search Use Flask-WTF for add-to-cart form with CSRF protection Handle image uploads in admin with Pillow for resizing/optimization.
Implement session-based cart
Implement shopping cart using Flask sessions for guests and database for authenticated users
Create cart blueprint with routes: view_cart() to display cart contents, add_to_cart() to add product with quantity (validate stock availability, AJAX endpoint returning JSON), update_cart() to change quantity, remove_from_cart() to remove item, clear_cart() to empty cart
Store cart in session for guests: session["cart"] = [{product_id, quantity, price}]
For authenticated users, use CartItem model to persist cart in database
Create CartService class in app/services/cart.py with methods: add_item(product_id, quantity), update_quantity(product_id, quantity), remove_item(product_id), get_cart_items() returning list of cart items with product details, get_cart_total() calculating subtotal, get_cart_count() for badge
Build cart/index.html template showing: cart items table with product thumbnail, name, price, quantity input with +/- buttons, remove button, subtotal per item, cart summary with subtotal, estimated tax, estimated shipping, total, "Continue Shopping" and "Proceed to Checkout" buttons, empty cart state with message
Implement JavaScript for instant cart updates using Fetch API or AJAX
Add cart badge in header navbar showing item count. Sync guest cart to user cart on login
Validate product availability and price before adding to cart (prevent tampering).Build order processing
Build comprehensive checkout and order processing system Create checkout blueprint with routes: checkout() to display checkout form, process_order() to handle order submission Build multi-step checkout form in checkout/index.html with sections: 1) Customer info (email, phone), 2) Shipping address form (name, address, city, state, zip, country - use Flask-WTF for validation), 3) Shipping method selection (radio buttons for standard/express with prices), 4) Payment section (Stripe Elements integration), 5) Order review showing cart items, addresses, totals Create CheckoutForm using Flask-WTF with required validators, email validator, custom validators for zip code In process_order() route: validate form data, validate cart not empty, validate product stock availability, calculate totals (subtotal, tax based on state, shipping based on method), create Stripe PaymentIntent, handle payment confirmation, create Order record with unique order_number, create OrderItem records for each cart item, reduce product quantities in database, clear cart, send confirmation email using Flask-Mail, redirect to order confirmation page Build order_confirmation() route showing order details with order number, items, shipping info, estimated delivery date Create OrderService class for order logic Handle errors gracefully: insufficient stock, payment failures, validation errors. Require login or guest checkout with email Store shipping/billing addresses in order as JSON Implement order number generation (e.g., ORD-YYYYMMDD-XXXXX).
Integrate Stripe API
Integrate Stripe payment processing into Flask e-commerce app
Install stripe package: pip install stripe
Configure Stripe API keys in config.py from environment variables: STRIPE_PUBLIC_KEY, STRIPE_SECRET_KEY
Create payment blueprint or add routes to checkout blueprint: create_payment_intent() route to generate Stripe PaymentIntent with order amount in cents (POST endpoint, requires AJAX call from frontend), webhook() route to handle Stripe webhooks for payment events
In checkout template, include Stripe.js: <script src="https://js.stripe.com/v3/"></script>
Implement Stripe Elements for card input: create Stripe instance with publishable key, create card element, mount to DOM, style to match your design
On form submit: prevent default, call create_payment_intent() endpoint to get client secret, use stripe.confirmCardPayment(clientSecret, {payment_method: {card: cardElement}}) to process payment, handle success: save payment ID to order, show success message, redirect to confirmation page, handle errors: display user-friendly error messages (card declined, insufficient funds, etc), allow retry
Store payment information in Order model: payment_method="stripe", payment_status="completed", payment_id=stripe_payment_id
Implement webhook handler to update order status on payment events: payment_intent.succeeded updates payment_status to completed, payment_intent.payment_failed marks as failed
Verify webhook signatures for security
Test with Stripe test cards (4242424242424242)
Handle refunds in admin panel if needed
Ensure PCI compliance by never storing card data directly - always use Stripe Elements.Build API endpoints
Build RESTful API endpoints for e-commerce operations
Create api blueprint with routes for: GET /api/products - list all active products with pagination, filtering, sorting (return JSON), GET /api/products/<id> - get single product details, GET /api/categories - list all categories, POST /api/cart/add - add item to cart (requires authentication or session), PUT /api/cart/update - update cart item quantity, DELETE /api/cart/remove - remove cart item, GET /api/cart - get cart contents with totals, POST /api/orders - create order (for mobile app or SPA frontend)
Implement API authentication using Flask-Login for session-based or JWT tokens for stateless API
Create API response helpers: success_response(data, status=200), error_response(message, status=400)
Add request validation: check required fields, validate data types, return 400 Bad Request for invalid data
Implement rate limiting using Flask-Limiter to prevent abuse
Add CORS support if API will be used by separate frontend: pip install flask-cors, configure CORS(app, resources={r"/api/*": {"origins": "*"}}). Document API endpoints with docstrings or Swagger/OpenAPI
Test API endpoints with curl or Postman. Return proper HTTP status codes: 200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error
Implement pagination for list endpoints: ?page=1&per_page=20
Add filtering: ?category=electronics&min_price=100. Return consistent JSON structure: {success: true, data: {}, message: ""}.Product Image Upload & Galleries
Add comprehensive product image management for e-commerce store.
Install Pillow for image processing: pip install Pillow.
Configure upload settings in config.py: UPLOAD_FOLDER = 'static/uploads/products', MAX_CONTENT_LENGTH = 10 * 1024 * 1024 (10MB for product images), ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'}.
Create organized directory structure: static/uploads/products/originals, static/uploads/products/large, static/uploads/products/medium, static/uploads/products/thumbnails.
Update Product model for multiple images: create ProductImage model with fields (product_id, image_path, thumbnail_path, medium_path, display_order, is_primary, alt_text), relationship to Product (one-to-many).
Implement image upload in admin: create multi-upload route accepting multiple files, validate each image (type, size, dimensions), generate unique filenames using uuid.
Use Pillow for image optimization: create multiple sizes - thumbnail (200x200), medium (600x600), large (1200x1200), maintain aspect ratio with thumbnail() or resize(), compress with optimize=True and quality=85, convert to WebP with save(format='WEBP') for modern browsers, keep original JPEG/PNG as fallback.
Build ImageService class for reusable logic: upload_product_image(file, product_id) - saves and creates all sizes, delete_product_image(image_id) - removes all file variants, reorder_images(product_id, new_order) - updates display order.
Create admin image management interface: multi-file upload form using JavaScript (Dropzone.js or FilePond), show image preview grid after upload, drag-and-drop to reorder images with AJAX persistence, set primary product image with radio buttons or star icon, add alt text fields for accessibility, delete individual images with confirmation.
For product listing pages: display primary product image on cards, implement hover effect showing second image if available, lazy load images using loading="lazy" attribute, show placeholder for products without images.
Build product detail gallery: create main large image display area with thumbnail strip below or sidebar, click thumbnail to change main image using JavaScript, implement image zoom on hover or click - magnifying glass effect using CSS transform and JavaScript mouse tracking, or use library like Drift.js, add full-screen lightbox for detailed viewing (PhotoSwipe, GLightbox).
Add responsive image serving in templates: use <picture> element with WebP source and JPEG fallback, implement srcset for responsive sizes: <img srcset="thumbnail-200.jpg 200w, medium-600.jpg 600w, large-1200.jpg 1200w" sizes="..." />, serve appropriate size based on viewport.
Implement image variants for product options: if products have color variants, link images to specific product variants, switch gallery when user selects different color/style, preload variant images for instant display.
Add image metadata and SEO: require alt text for all images in admin, implement schema.org Product markup with image URLs, include multiple product images in structured data, optimize image filenames (use product name slugs).
Handle missing images: create attractive placeholder image for products without photos, ensure consistent aspect ratios across all products, add "upload image" prompt in admin for imageless products.
Implement image cleanup: delete orphaned images when products deleted, create management command to find and remove unused images, schedule periodic cleanup with cron or Celery beat.
For better performance: implement CDN integration (Cloudinary, AWS CloudFront), lazy load non-primary images, use progressive JPEG format, optimize on upload to reduce storage costs.Testing Your Flask E-Commerce Application
Configure pytest
Set up comprehensive testing with pytest for Flask e-commerce application Install pytest, pytest-flask, pytest-cov for coverage, pytest-mock for mocking Create tests/ directory with __init__.py, conftest.py for fixtures, and test files organized by feature: test_products.py, test_cart.py, test_checkout.py, test_api.py, test_models.py In conftest.py, create fixtures: app (Flask app with testing config), client (test client from app.test_client()), db (database with create_all/drop_all), init_database (create test data), auth_client (authenticated client) Configure test database to use SQLite in-memory for speed: SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:" or separate test database. Set app.config["TESTING"] = True, disable CSRF for testing, disable email sending Create pytest.ini for pytest configuration: [pytest] testpaths = tests, python_files = test_*.py Add test command to requirements-dev.txt Create factories or fixtures for creating test data: create_user(), create_product(), create_order() Set up coverage reporting with pytest --cov=app --cov-report=html. Mock external services: Stripe API calls, email sending Write tests for models, routes, business logic, API endpoints, forms, helpers.
Test product routes
Write your first tests for product routes in tests/test_products.py
Test shop index page: test_shop_index_loads() - use client.get("/shop") to make request, assert response.status_code == 200, assert b"Products" in response.data or check for expected text, test pagination appears with multiple products
Test that only active products display: create products with is_active=True and is_active=False using fixtures, assert active products appear, assert inactive products do not appear using assert b"inactive_product_name" not in response.data
Test product detail page: test_product_detail_loads() - create product with fixture, visit client.get(f"/shop/product/{product.slug}"), assert 200 status, assert product name, price, description appear in response.data, assert add-to-cart form exists
Test 404 for non-existent product: test_product_not_found() - assert client.get("/shop/product/nonexistent").status_code == 404
Test product search: create products, test search query returns correct results
Use pytest fixtures for database setup/teardown. Mock database queries if needed for unit tests
Run tests with pytest -v and verify all pass
Use response.get_data(as_text=True) for string comparison.Test API endpoints
Write comprehensive tests for API endpoints in tests/test_api.py
Test GET /api/products: test_api_products_list() - make request with client.get("/api/products"), assert response.status_code == 200, assert response.content_type == "application/json", parse JSON with json.loads(response.data), assert response structure has success, data, products list, verify product data includes id, name, price, slug
Test pagination: assert response includes page, per_page, total
Test filtering: test_api_products_filter() - request with query params ?category=electronics, assert only filtered products returned
Test GET /api/products/<id>: assert returns single product, assert 404 for invalid ID
Test cart API endpoints: test_api_add_to_cart() - POST to /api/cart/add with JSON data {product_id, quantity}, assert successful response, assert cart updated
Test authentication required: test_api_cart_requires_auth() - attempt cart operations without authentication, assert 401 Unauthorized
Test validation: test_api_add_to_cart_invalid_data() - send invalid data (missing fields, negative quantity, invalid product_id), assert 400 Bad Request with error message
Test CORS headers if configured
Test rate limiting if implemented
Use pytest fixtures to create test data (products, users). Mock Stripe for payment tests
Test error responses return consistent JSON structure
Run tests with pytest tests/test_api.py -v.Deploying Your Flask E-Commerce to Production
Deploy to Heroku/DigitalOcean
Deploy Flask e-commerce app to production For Heroku: install Heroku CLI, create Heroku app (heroku create shop-name), add PostgreSQL addon (heroku addons:create heroku-postgresql:mini), create Procfile with "web: gunicorn app:app", create runtime.txt specifying Python version (python-3.11.0), set environment variables (heroku config:set SECRET_KEY=xxx STRIPE_SECRET_KEY=xxx), push to Heroku (git push heroku main), run migrations (heroku run flask db upgrade) For DigitalOcean App Platform: connect GitHub repo, configure build settings (pip install -r requirements.txt), set environment variables in dashboard, configure health checks For DigitalOcean Droplet: provision Ubuntu server, install Python, PostgreSQL, Nginx, set up virtual environment, clone repository, install dependencies, configure Gunicorn systemd service, set up Nginx reverse proxy, configure SSL with Let's Encrypt. Pre-deployment checklist: set DEBUG=False in production config, use environment variables for all secrets, migrate database schema (flask db upgrade), test with production-like data, optimize static assets, configure logging (to file or service like Papertrail), set up error tracking (Sentry), test Stripe in live mode. Post-deployment: monitor application logs, verify payments work end-to-end, set up database backups, configure CDN for static assets (Cloudflare), implement monitoring (UptimeRobot, Pingdom), load test with locust or similar tool. Use environment-specific configs (development, staging, production). Consider containerization with Docker for easier deployments.
