Blog

  • Python Fastapi Async Crud Operations Tutorial

    Welcome to the ultimate Python FastAPI async CRUD operations tutorial! If you’re looking to build high‑performance APIs that can handle thousands of concurrent requests, FastAPI is the perfect framework. In this guide, we’ll walk through everything you need to know—from setting up an async‑ready project to implementing Create, Read, Update, and Delete (CRUD) endpoints with proper validation, database integration, and best‑practice patterns. By the end, you’ll have a production‑ready codebase you can extend for any web service.

    Why Choose FastAPI for Async CRUD?

    FastAPI combines several modern Python features that make async CRUD development a breeze:

    • Native async support: Built on Starlette, FastAPI lets you write async def endpoints that run on an event loop, delivering non‑blocking I/O.
    • Automatic data validation: Pydantic models validate request bodies and query parameters out of the box.
    • Interactive documentation: Swagger UI and ReDoc are generated automatically, saving you time on API docs.
    • Speed: Benchmarks show FastAPI rivals Node.js and Go for throughput, especially when using async database drivers.

    Project Setup – Getting Started in Minutes

    1. Create a virtual environment

    python -m venv venv
    source venv/bin/activate  # On Windows use `venv\Scripts\activate`
    

    2. Install required packages

    pip install fastapi[all] uvicorn sqlalchemy aiosqlite asyncpg
    

    We’ll use SQLAlchemy 2.0 with the async extension, aiosqlite for a quick SQLite demo, and asyncpg if you switch to PostgreSQL later.

    3. Project structure

    my_fastapi_app/
    ├── app/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── db.py
    │   ├── models.py
    │   ├── schemas.py
    │   └── crud.py
    └── requirements.txt
    

    Database Configuration – Async SQLAlchemy

    First, define the async engine and session maker in db.py:

    from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
    from sqlalchemy.orm import sessionmaker, declarative_base
    
    DATABASE_URL = "sqlite+aiosqlite:///./test.db"  # Change to PostgreSQL if needed
    
    engine = create_async_engine(DATABASE_URL, echo=True)
    
    AsyncSessionLocal = sessionmaker(
        bind=engine,
        class_=AsyncSession,
        expire_on_commit=False,
    )
    
    Base = declarative_base()
    

    Defining Models and Schemas

    SQLAlchemy model

    # models.py
    from sqlalchemy import Column, Integer, String
    from .db import Base
    
    class Item(Base):
        __tablename__ = "items"
    
        id = Column(Integer, primary_key=True, index=True)
        name = Column(String, index=True, nullable=False)
        description = Column(String, nullable=True)
        price = Column(Integer, nullable=False)
    

    Pydantic schemas

    # schemas.py
    from pydantic import BaseModel, Field
    
    class ItemBase(BaseModel):
        name: str = Field(..., example="Laptop")
        description: str | None = Field(None, example="A powerful gaming laptop")
        price: int = Field(..., gt=0, example=1500)
    
    class ItemCreate(ItemBase):
        pass
    
    class ItemUpdate(BaseModel):
        name: str | None = None
        description: str | None = None
        price: int | None = None
    
    class ItemRead(ItemBase):
        id: int
    
        class Config:
            orm_mode = True
    

    CRUD Utility Functions

    Keeping database logic separate from the API layer improves testability. In crud.py we implement async CRUD helpers:

    # crud.py
    from sqlalchemy import select, update, delete
    from sqlalchemy.ext.asyncio import AsyncSession
    from . import models, schemas
    
    async def get_item(db: AsyncSession, item_id: int):
        result = await db.execute(select(models.Item).where(models.Item.id == item_id))
        return result.scalar_one_or_none()
    
    async def get_items(db: AsyncSession, skip: int = 0, limit: int = 100):
        result = await db.execute(select(models.Item).offset(skip).limit(limit))
        return result.scalars().all()
    
    async def create_item(db: AsyncSession, item: schemas.ItemCreate):
        db_item = models.Item(**item.dict())
        db.add(db_item)
        await db.commit()
        await db.refresh(db_item)
        return db_item
    
    async def update_item(db: AsyncSession, item_id: int, item: schemas.ItemUpdate):
        stmt = (
            update(models.Item)
            .where(models.Item.id == item_id)
            .values(**{k: v for k, v in item.dict().items() if v is not None})
            .execution_options(synchronize_session="fetch")
        )
        await db.execute(stmt)
        await db.commit()
        return await get_item(db, item_id)
    
    async def delete_item(db: AsyncSession, item_id: int):
        stmt = delete(models.Item).where(models.Item.id == item_id)
        await db.execute(stmt)
        await db.commit()
        return {"ok": True}
    

    FastAPI Endpoints – Wiring Everything Together

    Now we can expose the CRUD operations via async routes in main.py:

    # main.py
    from fastapi import FastAPI, Depends, HTTPException, status
    from sqlalchemy.ext.asyncio import AsyncSession
    from . import db, models, schemas, crud
    
    app = FastAPI(title="Async CRUD with FastAPI", version="1.0.0")
    
    # Dependency that provides an async DB session
    async def get_db() -> AsyncSession:
        async with db.AsyncSessionLocal() as session:
            yield session
    
    @app.on_event("startup")
    async def startup():
        # Create tables on startup (only for demo; use Alembic in prod)
        async with db.engine.begin() as conn:
            await conn.run_sync(models.Base.metadata.create_all)
    
    @app.post("/items/", response_model=schemas.ItemRead, status_code=status.HTTP_201_CREATED)
    async def create_item(item: schemas.ItemCreate, db: AsyncSession = Depends(get_db)):
        return await crud.create_item(db, item)
    
    @app.get("/items/", response_model=list[schemas.ItemRead])
    async def read_items(skip: int = 0, limit: int = 100, db: AsyncSession = Depends(get_db)):
        return await crud.get_items(db, skip, limit)
    
    @app.get("/items/{item_id}", response_model=schemas.ItemRead)
    async def read_item(item_id: int, db: AsyncSession = Depends(get_db)):
        db_item = await crud.get_item(db, item_id)
        if not db_item:
            raise HTTPException(status_code=404, detail="Item not found")
        return db_item
    
    @app.put("/items/{item_id}", response_model=schemas.ItemRead)
    async def update_item(item_id: int, item: schemas.ItemUpdate, db: AsyncSession = Depends(get_db)):
        db_item = await crud.get_item(db, item_id)
        if not db_item:
            raise HTTPException(status_code=404, detail="Item not found")
        return await crud.update_item(db, item_id, item)
    
    @app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
    async def delete_item(item_id: int, db: AsyncSession = Depends(get_db)):
        db_item = await crud.get_item(db, item_id)
        if not db_item:
            raise HTTPException(status_code=404, detail="Item not found")
        await crud.delete_item(db, item_id)
        return None
    

    Running the Application

    Start the server with Uvicorn, which is an ASGI‑compatible web server optimized for async workloads:

    uvicorn app.main:app --reload
    

    The --reload flag watches for file changes, making development fast and painless. Open http://127.0.0.1:8000/docs to explore the automatically generated Swagger UI, or /redoc for ReDoc.

    Testing Async Endpoints

    FastAPI works seamlessly with pytest and httpx for async testing. Below is a minimal test suite:

    # test_main.py
    import pytest
    from httpx import AsyncClient
    from app.main import app
    from app.db import engine, Base
    
    @pytest.fixture(scope="module", autouse=True)
    async def create_test_db():
        async with engine.begin() as conn:
            await conn.run_sync(Base.metadata.drop_all)
            await conn.run_sync(Base.metadata.create_all)
        yield
        async with engine.begin() as conn:
            await conn.run_sync(Base.metadata.drop_all)
    
    @pytest.mark.asyncio
    async def test_create_item():
        async with AsyncClient(app=app, base_url="http://test") as client:
            payload = {"name": "Keyboard", "price": 99}
            response = await client.post("/items/", json=payload)
            assert response.status_code == 201
            data = response.json()
            assert data["name"] == "Keyboard"
            assert data["price"] == 99
    

    Best Practices for Production‑Ready Async CRUD

    • Use a real async driver: Switch from SQLite/aiosqlite to PostgreSQL/asyncpg for scalability.
    • Employ Alembic migrations: Manage schema changes without dropping tables on startup.
    • Configure connection pooling: Adjust pool_size and max_overflow in the engine URL for optimal throughput.
    • Validate input thoroughly: Leverage Pydantic validators for complex business rules.
    • Secure endpoints: Add OAuth2, JWT, or API key dependencies using FastAPI’s security utilities.
    • Implement logging and monitoring: Integrate structured logs (e.g., Loguru
  • Python Django Signals And Middleware Guide

    When building robust web applications with Python Django, two powerful concepts often surface: signals and middleware. Both enable you to hook into the request‑response cycle, react to events, and keep your code clean and reusable. In this guide we’ll explore what Django signals and middleware are, when to use each, how to implement them correctly, and best‑practice tips that will help you write maintainable, SEO‑friendly Django projects.

    Understanding Django Signals

    What are Django signals?

    Signals are a publish‑subscribe mechanism that lets decoupled applications get notified when certain actions occur. Django ships with a built‑in Signal class that can broadcast events such as model saves, user logins, or migrations. Listeners (called receivers) connect to these signals and execute custom logic without tightly coupling to the source code.

    Common built‑in signals

    • pre_save / post_save – triggered before or after a model instance is saved.
    • pre_delete / post_delete – fired around model deletions.
    • m2m_changed – emitted when a many‑to‑many relationship is altered.
    • request_started / request_finished – lifecycle hooks for HTTP requests.
    • user_logged_in, user_logged_out, user_login_failed – user authentication events.

    Creating a custom signal

    Sometimes the built‑in signals don’t cover your specific use case. Django makes it easy to define and dispatch your own signals.

    # myapp/signals.py
    from django.dispatch import Signal
    
    # Define a signal that provides the user and a custom message
    user_action = Signal(providing_args=['user', 'message'])
    

    Next, connect a receiver:

    # myapp/receivers.py
    from django.dispatch import receiver
    from .signals import user_action
    
    @receiver(user_action)
    def log_user_action(sender, **kwargs):
        user = kwargs.get('user')
        message = kwargs.get('message')
        # Example: write to a log file or external service
        print(f'User {user.username} performed action: {message}')
    

    Finally, dispatch the signal wherever needed:

    # some_view.py
    from .signals import user_action
    
    def some_view(request):
        # ... view logic ...
        user_action.send(sender=None, user=request.user, message='Created a new post')
        return HttpResponse('Success')
    

    Best practices for using signals

    • Keep receivers lightweight – heavy processing can slow down the request/response cycle.
    • Prefer post_save over pre_save when you need the object’s primary key.
    • Register receivers in apps.py ready() method to avoid duplicate imports.
    • Document each custom signal clearly; they act as an API for other developers.

    Django Middleware Explained

    What is middleware?

    Middleware is a stack of classes that process incoming requests and outgoing responses. Each middleware component can modify the request before it reaches the view, or alter the response before it’s sent to the client. This pattern is ideal for cross‑cutting concerns such as security, caching, and request logging.

    Built‑in middleware categories

    • Security middleware – e.g., SecurityMiddleware adds HSTS headers.
    • Session middleware – manages user sessions via cookies.
    • Authentication middleware – attaches request.user to each request.
    • Message middleware – enables the Django messages framework.
    • Common middleware – handles URL normalization and ETag generation.

    Writing custom middleware

    A custom middleware class must implement at least one of the following methods: __init__, process_request, process_view, process_template_response, process_response, or the newer __call__ style. The modern approach uses a callable that returns a response.

    # myapp/middleware.py
    from django.utils.deprecation import MiddlewareMixin
    
    class SimpleTimingMiddleware(MiddlewareMixin):
        def process_request(self, request):
            request.start_time = time.time()
    
        def process_response(self, request, response):
            duration = time.time() - getattr(request, 'start_time', time.time())
            response['X-Processing-Time-ms'] = int(duration * 1000)
            return response
    

    Or using the newer __call__ pattern:

    # myapp/middleware.py
    import time
    
    class SimpleTimingMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            start = time.time()
            response = self.get_response(request)
            duration = (time.time() - start) * 1000
            response['X-Processing-Time-ms'] = f'{int(duration)}'
            return response
    

    Don’t forget to add the middleware path to settings.MIDDLEWARE in the order you want it to execute.

    When to choose middleware over signals

    • Use middleware for request/response transformations, security headers, or global logging.
    • Use signals for model‑level events, user authentication hooks, or any decoupled business logic that isn’t tied to HTTP flow.

    Debugging and Testing Signals & Middleware

    Testing signals

    Leverage Django’s Signal.disconnect method in test setup to isolate side effects, and use assertLogs or mock objects to verify that receivers fire as expected.

    from unittest.mock import patch
    from myapp.signals import user_action
    
    def test_user_action_signal():
        with patch('myapp.receivers.log_user_action') as mock_receiver:
            user_action.send(sender=None, user=mock_user, message='Test')
            mock_receiver.assert_called_once()
    

    Testing middleware

    Use Django’s Client or RequestFactory to simulate requests and assert that your middleware modifies the response correctly.

    from django.test import RequestFactory, SimpleTestCase
    from myapp.middleware import SimpleTimingMiddleware
    
    class TimingMiddlewareTest(SimpleTestCase):
        def setUp(self):
            self.factory = RequestFactory()
            self.get_response = lambda request: HttpResponse('OK')
            self.middleware = SimpleTimingMiddleware(self.get_response)
    
        def test_processing_time_header(self):
            request = self.factory.get('/')
            response = self.middleware(request)
            self.assertIn('X-Processing-Time-ms', response)
    

    Performance and SEO Considerations

    • Minimize latency: Heavy signal receivers or middleware that perform DB queries can increase page load time, harming SEO rankings.
    • Cache expensive results inside middleware and reuse them across requests when possible.
    • Use async middleware (available in Django 4.0+) for I/O‑bound tasks to keep the event loop responsive.
    • Ensure that any added HTTP headers (e.g., security or timing headers) do not conflict with search‑engine crawlers.

    Real‑world examples

    Example 1: Auto‑populate a slug field with a signal

    # blog/models.py
    from django.db import models
    from django.utils.text import slugify
    from django.db.models.signals import pre_save
    from django.dispatch import receiver
    
    class Post(models.Model):
        title = models.CharField(max_length=200)
        slug = models.SlugField(unique=True, blank=True)
    
    @receiver(pre_save, sender=Post)
    def set_slug(sender, instance, **kwargs):
        if not instance.slug:
            instance.slug = slugify(instance.title)
    

    Example 2: Enforce HTTPS via middleware

    # security/middleware.py
    class EnforceHTTPSMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            if not request.is_secure():
                secure_url = request.build_absolute_uri().replace('http://', 'https://')
                return HttpResponsePermanentRedirect(secure_url)
            return self.get_response(request)
    

    Key Takeaways

    Both signals and middleware are essential tools in the Django toolbox. Signals excel at decoupling model‑level events, while middleware shines for request/response manipulation and global concerns. By following the best practices outlined above—keeping receivers lightweight, registering them properly, writing clear middleware, and testing thoroughly—you’ll build Django applications that are maintainable, performant, and SEO‑friendly.

    Conclusion

    Mastering Django signals and middleware unlocks a new level of flexibility in your Python web projects. Whether you need to log user activity, automatically generate slugs, enforce security policies, or measure request latency, these patterns let you inject functionality cleanly without cluttering your core business logic. Start experimenting with the examples provided, adapt them to your own use cases, and

  • Python Django Redis Caching Performance

    When it comes to building fast, scalable web applications with Python, Django combined with Redis is a powerhouse duo. Developers constantly ask, “How can I boost my Django app’s performance?” The answer often lies in intelligent caching, and Redis is the go‑to in‑memory data store that makes it possible. In this article we’ll dive deep into Python Django Redis caching performance, explore why Redis outshines other cache back‑ends, walk through step‑by‑step configuration, and reveal practical tips to squeeze every last millisecond out of your stack.

    Why Caching Matters for Django Applications

    Django’s ORM, template rendering, and middleware layers add valuable abstraction, but they also introduce latency. Each database query, template compilation, or API call consumes CPU cycles and I/O bandwidth. Caching reduces repetitive work by storing the result of expensive operations in a fast, volatile store. The benefits are clear:

    • Reduced database load: Frequently accessed rows are served from memory.
    • Faster page renders: Template fragments and whole views can be retrieved instantly.
    • Improved user experience: Lower response times translate to higher conversion rates.
    • Scalability: Your app can handle more concurrent users without adding more database replicas.

    Redis vs. Other Cache Back‑Ends

    While Django ships with a simple in‑process LocMemCache and supports Memcached, Redis brings a richer feature set that directly impacts performance:

    Data Structures and Atomic Operations

    Redis isn’t just a key‑value store; it offers hashes, sets, sorted sets, and bitmaps. This allows you to cache complex objects without serialization overhead, and perform atomic increments or expirations directly on the server.

    Persistence Options

    With RDB snapshots and AOF logs, Redis can survive restarts, giving you a safety net that pure in‑memory caches lack. You can even configure a hybrid approach where hot data stays in memory while less‑used entries are persisted to disk.

    Built‑in Pub/Sub and Streams

    Cache invalidation becomes easier when you can broadcast invalidation messages across multiple Django workers using Redis Pub/Sub. This eliminates stale data without manual coordination.

    Setting Up Redis for Django

    Follow these steps to get Redis up and running with Django:

    1. Install Redis Server on your development or production machine.
      sudo apt-get update
      sudo apt-get install redis-server
    2. Install the Python client (django‑redis) via pip.
      pip install django-redis
    3. Configure Django settings to use Redis as the cache back‑end.
      # settings.py
      CACHES = {
          "default": {
              "BACKEND": "django_redis.cache.RedisCache",
              "LOCATION": "redis://127.0.0.1:6379/1",
              "OPTIONS": {
                  "CLIENT_CLASS": "django_redis.client.DefaultClient",
                  # Optional: enable connection pooling
                  "CONNECTION_POOL_KWARGS": {"max_connections": 100},
              },
              "TIMEOUT": 300,  # default timeout in seconds
          }
      }
    4. Test the connection using Django’s shell.
      python manage.py shell
      >>> from django.core.cache import cache
      >>> cache.set('test_key', 'hello redis', 60)
      >>> cache.get('test_key')
      'hello redis'

    Cache Strategies that Maximize Performance

    Choosing the right caching strategy is as important as the technology itself. Below are the most effective patterns for Django projects.

    1. Per‑View Caching

    Cache the entire response of a view that doesn’t change per user. Use Django’s @cache_page decorator:

    from django.views.decorators.cache import cache_page
    
    @cache_page(60 * 15)  # cache for 15 minutes
    def popular_articles(request):
        # heavy DB query here
        return render(request, "articles/popular.html")

    2. Template Fragment Caching

    When only parts of a page are static, wrap those fragments in {% cache %} tags.

    {% load cache %}
    {% cache 300 sidebar %}
        {% include "partials/sidebar.html" %}
    {% endcache %}

    3. Low‑Level Cache API

    For custom data structures—like a list of recent comments—use the low‑level API:

    # utils.py
    from django.core.cache import cache
    
    def get_recent_comments():
        comments = cache.get('recent_comments')
        if comments is None:
            comments = Comment.objects.order_by('-created')[:20]
            cache.set('recent_comments', comments, 120)  # cache for 2 minutes
        return comments

    4. Cache Invalidation with Signals

    Keep cache fresh by tying invalidation to model signals:

    from django.db.models.signals import post_save, post_delete
    from django.dispatch import receiver
    
    @receiver([post_save, post_delete], sender=Article)
    def clear_article_cache(sender, **kwargs):
        cache.delete('article_list')

    Measuring and Optimizing Redis Cache Performance

    Having a cache is only half the battle; you need metrics to ensure it actually improves latency.

    Key Metrics to Track

    • Hit Ratio: Percentage of requests served from cache. Aim for >80% for high‑traffic endpoints.
    • Latency: Time taken for GET and SET operations. Sub‑millisecond latency is typical for local Redis instances.
    • Memory Usage: Monitor used_memory and maxmemory_policy to avoid evictions.
    • Eviction Rate: High evictions indicate an undersized cache; consider increasing maxmemory or tuning TTLs.

    Tools for Monitoring

    Redis ships with redis-cli INFO, but for production you’ll want dashboards like:

    • RedisInsight: GUI for real‑time stats and slow‑log analysis.
    • Prometheus + Grafana: Export redis_exporter metrics and visualize hit ratio trends.
    • Apm tools (e.g., New Relic, Datadog): Correlate cache metrics with Django request timings.

    Performance Tuning Tips

    • Enable Connection Pooling: Reduces the overhead of establishing TCP connections.
    • Use Appropriate Data Types: Store counters in INCR instead of serializing Python integers.
    • Adjust maxmemory-policy: Choose allkeys-lru for most use‑cases to automatically evict least‑recently‑used keys.
    • Pipeline Commands: Batch multiple GET/SET calls to cut round‑trip latency.
    • Compress Large Values: For big JSON blobs, compress with zlib before caching.

    Real‑World Example: Caching a Complex Queryset

    Suppose you have an e‑commerce site that displays a “Top Sellers” list. The query joins three tables and aggregates sales data, taking ~250 ms on a moderately sized database. By caching the result, you can drop response time to under 5 ms.

    # views.py
    from django.core.cache import cache
    from django.db.models import Sum
    from .models import OrderItem
    
    def top_sellers(request):
        cache_key = "top_sellers_30min"
        data = cache.get(cache_key)
        if not data:
            data = (
                OrderItem.objects
                .values('product_id')
                .annotate(total_sales=Sum('quantity'))
                .order_by('-total_sales')[:10]
            )
            # Store as list of dicts to avoid lazy queryset pickling
            cache.set(cache_key, list(data), 1800)  # 30 minutes
        return render(request, "shop/top_sellers.html", {"top_sellers": data})

    After deploying this cache, monitoring shows the hit ratio for top_sellers_30min at 95%, and the average view latency drops from 250 ms to 4 ms—a dramatic improvement.

    Common Pitfalls and How to Avoid Them

    • Cache Stampede: When a key expires, many workers may query the DB simultaneously. Mitigate with django-redis’s LOCK_TIMEOUT or implement “lazy” regeneration.
    • Stale Data: Overly long TTLs can serve outdated content. Pair TTLs with signal‑based invalidation for critical data.
    • Memory Bloat: Caching large objects without size limits can exhaust RAM. Use MAX_ENTRIES or set explicit MAX_MEMORY in Redis.
    • Serialization Overhead: Pickling complex Python objects is slower than storing native Redis structures. Prefer JSON or native hashes when possible.
    • Improper Key Naming: Use namespaced keys (e.g., user:42:profile) to avoid collisions and simplify debugging.

    Scaling Redis for High‑Traffic Django Sites

    When a single Redis instance becomes a bottleneck, consider these scaling strategies:

    • Read Replicas: Use Redis replication to offload read‑heavy workloads.
  • Python Django Celery Background Tasks

    When your Django application starts to grow, handling time‑consuming operations—like sending emails, processing images, or crunching data—inside the request‑response cycle can quickly become a performance bottleneck. This is where Celery steps in, turning heavyweight jobs into background tasks that run asynchronously, keeping your web server fast and your users happy. In this guide we’ll explore everything you need to know to integrate Python Django with Celery, from installation to best practices, so you can build scalable, reliable applications that handle background work like a pro.

    Why Use Celery with Django?

    Celery is the de‑facto standard task queue for Python, and it pairs naturally with Django for several reasons:

    • Asynchronous execution: Offload long‑running jobs to worker processes, freeing up request threads.
    • Scalability: Add more workers or nodes without changing your Django code.
    • Reliability: Built‑in retry mechanisms, result backends, and task acknowledgments.
    • Scheduling: Use Celery Beat to run periodic tasks (cron‑like jobs) directly from your Django project.

    These capabilities make Celery an essential tool for any production‑grade Django site that needs to handle background processing efficiently.

    Core Concepts of Celery

    Broker

    The broker is the messaging layer that transports tasks from Django to Celery workers. Popular choices include Redis and RabbitMQ. The broker must be highly available and fast, as it’s the backbone of the task queue.

    Worker

    A Celery worker is a separate process (or set of processes) that pulls tasks from the broker, executes them, and optionally stores results in a result backend.

    Result Backend

    After a task finishes, Celery can store its outcome in a backend such as Redis, a database, or Amazon S3. This is useful for tracking status, retries, or returning data to the user.

    Task

    A task is a Python callable (usually a function) decorated with @shared_task or @app.task. When you call my_task.delay(arg1, arg2), Celery serializes the request and sends it to the broker.

    Setting Up Celery in a Django Project

    1. Install Dependencies

    pip install celery[redis] django-celery-beat
    

    The django-celery-beat package provides a Django admin interface for managing periodic tasks.

    2. Create a Celery Configuration File

    In your Django project root (next to settings.py), create celery.py:

    import os
    from celery import Celery
    
    # Set default Django settings module for 'celery' program.
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
    
    app = Celery('myproject')
    
    # Using a string here means the worker doesn’t have to serialize
    # the configuration object to child processes.
    app.config_from_object('django.conf:settings', namespace='CELERY')
    
    # Auto-discover tasks from all installed apps.
    app.autodiscover_tasks()
    

    3. Update settings.py

    # Celery broker (Redis example)
    CELERY_BROKER_URL = 'redis://localhost:6379/0'
    
    # Optional result backend
    CELERY_RESULT_BACKEND = 'redis://localhost:6379/1'
    
    # Accept JSON only (safer than pickle)
    CELERY_ACCEPT_CONTENT = ['json']
    CELERY_TASK_SERIALIZER = 'json'
    CELERY_RESULT_SERIALIZER = 'json'
    
    # Timezone settings
    CELERY_TIMEZONE = 'UTC'
    CELERY_ENABLE_UTC = True
    

    4. Initialize Celery with Django

    Add the following line at the bottom of myproject/__init__.py so Django loads Celery when it starts:

    from .celery import app as celery_app
    
    __all__ = ('celery_app',)
    

    Creating and Running Background Tasks

    Defining a Task

    Inside any app, create a tasks.py file and define tasks using the @shared_task decorator:

    from celery import shared_task
    import time
    
    @shared_task
    def send_welcome_email(user_id):
        # Simulate a slow email service
        time.sleep(5)
        # Actual email logic would go here
        return f'Welcome email sent to user {user_id}'
    

    Calling a Task Asynchronously

    From a view or signal, enqueue the task with .delay() or .apply_async():

    def register_user(request):
        # ... user registration logic ...
        send_welcome_email.delay(new_user.id)
        return HttpResponse('Registration successful')
    

    Passing Arguments Safely

    • Use JSON‑serializable data types (int, str, list, dict).
    • Avoid sending large objects; store them in the database and pass the primary key instead.

    Scheduling Periodic Tasks with Celery Beat

    Enable Django‑Admin Management

    After installing django-celery-beat, add it to INSTALLED_APPS and run migrations:

    INSTALLED_APPS += ['django_celery_beat']
    python manage.py migrate django_celery_beat
    

    Define a Periodic Task in Code

    from celery.schedules import crontab
    
    app.conf.beat_schedule = {
        'clear-expired-sessions-every-midnight': {
            'task': 'myapp.tasks.clear_expired_sessions',
            'schedule': crontab(hour=0, minute=0),
        },
    }
    

    Or Create It via the Admin UI

    Navigate to **Django admin → Periodic Tasks** and fill out:

    • Name: Descriptive identifier.
    • Task: Fully qualified task name (e.g., myapp.tasks.clear_expired_sessions).
    • Schedule: Choose a crontab or interval schedule.
    • Enabled: Toggle on/off without code changes.

    Monitoring and Managing Tasks

    Flower – Real‑Time Web UI

    Install Flower to get a live dashboard of workers, queues, and task states:

    pip install flower
    celery -A myproject flower
    

    Visit http://localhost:5555 to view statistics, inspect failed tasks, and revoke jobs.

    Command‑Line Tools

    • celery -A myproject status – Check worker health.
    • celery -A myproject purge – Clear pending tasks (use with caution).
    • celery -A myproject inspect active – List currently running tasks.

    Logging

    Configure Django’s logging to capture Celery output:

    LOGGING = {
        'version': 1,
        'handlers': {
            'celery': {
                'class': 'logging.StreamHandler',
            },
        },
        'loggers': {
            'celery': {
                'handlers': ['celery'],
                'level': 'INFO',
            },
        },
    }
    

    Best Practices and Common Pitfalls

    1. Keep Tasks Small and Idempotent

    Break large jobs into smaller, repeatable units. This reduces the impact of a failure and simplifies retries.

    2. Use Retries Wisely

    @shared_task(bind=True, max_retries=3, default_retry_delay=60)
    def fetch_external_api(self, url):
        try:
            response = requests.get(url, timeout=5)
            response.raise_for_status()
            return response.json()
        except requests.RequestException as exc:
            raise self.retry(exc=exc)
    

    3. Choose the Right Broker

    • Redis: Easy to set up, great for small to medium workloads.
    • RabbitMQ: Handles high‑throughput, complex routing, and guarantees delivery.

    4. Avoid Blocking Calls in Workers

    Never perform CPU‑intensive calculations inside a Celery worker unless you allocate dedicated concurrency (e.g., using --pool=solo or separate worker pools).

    5. Secure Your Broker

    Set strong passwords, enable TLS, and restrict network access to the broker. Exposed brokers are a common security risk.

    6. Monitor Task Latency

    High queue latency often indicates insufficient workers. Use Flower or Prom

  • Python Django Stripe Payment Integration

    Integrating Stripe payments into a Python Django application is one of the most effective ways to turn a simple web project into a revenue‑generating platform. In this guide we’ll walk through every step—from setting up a Stripe account to handling webhooks—so you can build a secure, scalable, and SEO‑friendly checkout experience that Google loves. Whether you’re launching a SaaS product, an e‑commerce store, or a subscription‑based service, mastering Django‑Stripe integration will give you the confidence to accept credit cards worldwide.

    Why Combine Django and Stripe?

    • Rapid development: Django’s batteries‑included philosophy pairs perfectly with Stripe’s well‑documented API.
    • Security compliance: Stripe handles PCI‑DSS requirements, letting you focus on business logic.
    • Scalable pricing: Pay‑as‑you‑go model works for startups and enterprises alike.
    • Rich feature set: One‑time payments, recurring subscriptions, saved cards, and Apple/Google Pay are all available out of the box.

    Prerequisites Before You Start

    1. Python 3.9+ and a virtual environment (venv or conda).
    2. Basic Django project already created (django-admin startproject mysite).
    3. A Stripe account (you can sign up for free at dashboard.stripe.com).
    4. Familiarity with Django models, views, and templates.

    Step 1 – Install Required Packages

    First, add the official Stripe Python library and Django‑Stripe helper packages to your environment.

    pip install stripe django-environ
    # Optional: django-widget-tweaks for nicer forms
    pip install django-widget-tweaks
    

    Step 2 – Configure Django Settings

    Store your Stripe keys securely using django-environ. Create a .env file in the project root:

    # .env
    STRIPE_PUBLIC_KEY=pk_test_XXXXXXXXXXXXXXXXXXXXXXXX
    STRIPE_SECRET_KEY=sk_test_XXXXXXXXXXXXXXXXXXXXXXXX
    STRIPE_WEBHOOK_SECRET=whsec_XXXXXXXXXXXXXXXXXXXXXXXX
    DEBUG=True
    

    Then load these variables in settings.py:

    import environ
    env = environ.Env()
    environ.Env.read_env()
    
    STRIPE_PUBLIC_KEY = env('STRIPE_PUBLIC_KEY')
    STRIPE_SECRET_KEY = env('STRIPE_SECRET_KEY')
    STRIPE_WEBHOOK_SECRET = env('STRIPE_WEBHOOK_SECRET')
    

    Step 3 – Create a Payment Model

    Even if you only need a one‑time charge, persisting transaction data is a best practice. Add a simple model to payments/models.py:

    from django.db import models
    from django.conf import settings
    
    class Charge(models.Model):
        stripe_charge_id = models.CharField(max_length=255, unique=True)
        amount = models.PositiveIntegerField(help_text="Amount in cents")
        currency = models.CharField(max_length=10, default='usd')
        email = models.EmailField()
        created_at = models.DateTimeField(auto_now_add=True)
        paid = models.BooleanField(default=False)
    
        def __str__(self):
            return f"{self.email} – {self.amount/100:.2f} {self.currency.upper()}"
    

    Run migrations:

    python manage.py makemigrations payments
    python manage.py migrate
    

    Step 4 – Build the Checkout View

    We’ll use Stripe Checkout, a hosted payment page that reduces PCI scope. Create a view in payments/views.py:

    import stripe
    from django.conf import settings
    from django.shortcuts import render, redirect
    from django.urls import reverse
    from .models import Charge
    
    stripe.api_key = settings.STRIPE_SECRET_KEY
    
    def create_checkout_session(request):
        if request.method == "POST":
            # Example: $29.99 one‑time payment
            amount = 2999
            domain = request.build_absolute_uri('/')[:-1]  # strip trailing slash
    
            checkout_session = stripe.checkout.Session.create(
                payment_method_types=['card'],
                line_items=[{
                    'price_data': {
                        'currency': 'usd',
                        'product_data': {
                            'name': 'Premium Subscription',
                        },
                        'unit_amount': amount,
                    },
                    'quantity': 1,
                }],
                mode='payment',
                success_url=domain + reverse('payments:success') + '?session_id={CHECKOUT_SESSION_ID}',
                cancel_url=domain + reverse('payments:cancel'),
            )
            # Store a placeholder Charge record (will be updated via webhook)
            Charge.objects.create(
                stripe_charge_id=checkout_session.payment_intent,
                amount=amount,
                email=request.POST.get('email', ''),
            )
            return redirect(checkout_session.url, code=303)
        return render(request, 'payments/checkout.html')
    

    Template Example (checkout.html)

    <form method="post">
        {% csrf_token %}
        <label for="email">Email:</label>
        <input type="email" name="email" id="email" required>
        <button type="submit">Pay $29.99</button>
    </form>
    

    Step 5 – Handle Success and Cancel Pages

    Add two simple views that display friendly messages and optionally retrieve the session details.

    def success(request):
        session_id = request.GET.get('session_id')
        session = stripe.checkout.Session.retrieve(session_id)
        return render(request, 'payments/success.html', {'session': session})
    
    def cancel(request):
        return render(request, 'payments/cancel.html')
    

    Step 6 – Secure Webhook Endpoint

    Stripe sends asynchronous events (e.g., checkout.session.completed) to a webhook URL you define. This is where you mark the charge as paid and trigger post‑payment actions such as sending a receipt.

    import json
    from django.http import HttpResponse, HttpResponseBadRequest
    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt
    def stripe_webhook(request):
        payload = request.body
        sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
        try:
            event = stripe.Webhook.construct_event(
                payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
            )
        except (ValueError, stripe.error.SignatureVerificationError):
            return HttpResponseBadRequest()
    
        # Handle the checkout.session.completed event
        if event['type'] == 'checkout.session.completed':
            session = event['data']['object']
            charge = Charge.objects.filter(stripe_charge_id=session['payment_intent']).first()
            if charge:
                charge.paid = True
                charge.save()
                # TODO: send email, activate account, etc.
    
        return HttpResponse(status=200)
    

    Step 7 – Register URLs

    Map the views in payments/urls.py and include them in the project’s main urls.py:

    # payments/urls.py
    from django.urls import path
    from . import views
    
    app_name = 'payments'
    
    urlpatterns = [
        path('checkout/', views.create_checkout_session, name='checkout'),
        path('success/', views.success, name='success'),
        path('cancel/', views.cancel, name='cancel'),
        path('webhook/', views.stripe_webhook, name='webhook'),
    ]
    

    Step 8 – Test in Stripe’s Sandbox

    • Enable Test Mode in the Stripe Dashboard.
    • Use test card numbers (e.g., 4242 4242 4242 4242) to simulate successful and declined payments.
    • Visit /payments/webhook/ on a publicly reachable URL (ngrok is handy): ngrok http 8000.
    • In the Dashboard, add the ngrok URL as the webhook endpoint and subscribe to checkout.session.completed.

    Step 9 – Deploy to Production

    When you’re ready to go live, switch the API keys in your .env file to the live keys, disable DEBUG, and ensure the webhook secret matches the production endpoint. Remember to set ALLOWED_HOSTS and use HTTPS—Stripe will reject non‑secure callbacks.

    Best Practices & Common Pitfalls

    Security Tips

    • Never hard‑code API keys; always load them from environment variables.
    • Validate webhook signatures to prevent forged events.
    • Store only the payment_intent ID; do not keep raw card data.

    Performance Optimizations

    • Cache the public key in your template context to avoid repeated settings lookups.
    • Use asynchronous tasks (Celery or Django Q) for post‑payment emails and provisioning.

    SEO Considerations

    Search engines love clear, keyword‑rich content. To boost the visibility of this guide, we’ve included the primary phrase Python Django Stripe payment integration multiple times, along with related terms such as “Stripe Checkout”, “Django webhook”, and “PCI‑DSS compliance”. Additionally, use descriptive meta tags

  • Python Django Social Media App From Scratch

    Building a social media platform from scratch may sound like a daunting task, but with Python Django’s robust framework, you can turn a simple idea into a fully‑featured web app in a matter of weeks. In this step‑by‑step guide we’ll walk through everything you need—from project setup and data modeling to real‑time updates and SEO best practices—so you can launch a scalable, secure, and SEO‑friendly social media app that stands out in today’s crowded digital landscape.

    Why Choose Django for a Social Media App?

    • Rapid Development: Django’s “batteries‑included” philosophy provides authentication, admin panels, and ORM out of the box.
    • Scalability: Built‑in support for caching, middleware, and asynchronous tasks makes it easy to grow with your user base.
    • Security: Automatic protection against XSS, CSRF, and SQL injection keeps user data safe.
    • Community & Packages: A massive ecosystem (e.g., django‑rest‑framework, django‑channels) speeds up feature implementation.
    • SEO‑Ready: Clean URLs, sitemap generation, and template tags help search engines index your content effectively.

    Project Setup: From Zero to Django Project

    Install Python and Create a Virtual Environment

    # Install the latest Python 3.x
    $ sudo apt-get install python3 python3-venv
    
    # Create and activate a virtual environment
    $ python3 -m venv env
    $ source env/bin/activate
    
    # Upgrade pip
    (env) $ pip install --upgrade pip
    

    Start a New Django Project

    # Install Django and supporting packages
    (env) $ pip install django djangorestframework django-allauth
    
    # Create the project
    (env) $ django-admin startproject socialapp
    (env) $ cd socialapp
    
    # Create the core app
    (env) $ python manage.py startapp core
    

    Don’t forget to add 'core', 'rest_framework', and 'allauth' to INSTALLED_APPS in settings.py, and configure the database (SQLite for development, PostgreSQL for production).

    Designing the Core Models

    User Profile Model

    While Django’s built‑in User model handles authentication, a separate Profile model lets you store extra information such as avatar, bio, and follower count.

    from django.contrib.auth.models import User
    from django.db import models
    
    class Profile(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE)
        avatar = models.ImageField(upload_to='avatars/', default='avatars/default.jpg')
        bio = models.TextField(max_length=500, blank=True)
        followers = models.ManyToManyField('self', symmetrical=False, related_name='following', blank=True)
    
        def __str__(self):
            return self.user.username
    

    Post, Comment, and Like Models

    These models form the backbone of any social feed.

    class Post(models.Model):
        author = models.ForeignKey(Profile, on_delete=models.CASCADE)
        content = models.TextField()
        image = models.ImageField(upload_to='posts/', blank=True, null=True)
        created_at = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return f'{self.author.user.username}: {self.content[:30]}'
    
    class Comment(models.Model):
        post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
        author = models.ForeignKey(Profile, on_delete=models.CASCADE)
        text = models.CharField(max_length=300)
        created_at = models.DateTimeField(auto_now_add=True)
    
    class Like(models.Model):
        post = models.ForeignKey(Post, related_name='likes', on_delete=models.CASCADE)
        user = models.ForeignKey(Profile, on_delete=models.CASCADE)
        created_at = models.DateTimeField(auto_now_add=True)
    
        class Meta:
            unique_together = ('post', 'user')
    

    Building the Key Features

    User Registration and Authentication

    Leverage django-allauth for a seamless sign‑up flow, social login, and email verification.

    # settings.py
    INSTALLED_APPS += [
        'django.contrib.sites',
        'allauth',
        'allauth.account',
        'allauth.socialaccount',
    ]
    
    SITE_ID = 1
    
    AUTHENTICATION_BACKENDS = (
        'django.contrib.auth.backends.ModelBackend',
        'allauth.account.auth_backends.AuthenticationBackend',
    )
    
    ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
    ACCOUNT_EMAIL_REQUIRED = True
    

    Run migrations and configure URLs:

    # urls.py
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('accounts/', include('allauth.urls')),
        path('', include('core.urls')),
    ]
    

    Creating Posts and Feeds

    Use Django’s generic class‑based views (CBVs) to handle CRUD operations efficiently.

    from django.views.generic import ListView, CreateView
    from django.contrib.auth.mixins import LoginRequiredMixin
    
    class FeedView(LoginRequiredMixin, ListView):
        model = Post
        template_name = 'core/feed.html'
        paginate_by = 10
    
        def get_queryset(self):
            # Show posts from followed users + self
            following = self.request.user.profile.following.all()
            return Post.objects.filter(author__in=following).order_by('-created_at')
    
    class PostCreateView(LoginRequiredMixin, CreateView):
        model = Post
        fields = ['content', 'image']
        template_name = 'core/post_create.html'
    
        def form_valid(self, form):
            form.instance.author = self.request.user.profile
            return super().form_valid(form)
    

    Implementing Likes and Comments

    AJAX calls keep the user experience fluid. Below is a simple Django view for toggling likes.

    from django.http import JsonResponse
    from django.views.decorators.http import require_POST
    from django.contrib.auth.decorators import login_required
    
    @require_POST
    @login_required
    def toggle_like(request, post_id):
        post = Post.objects.get(id=post_id)
        profile = request.user.profile
        liked, created = Like.objects.get_or_create(post=post, user=profile)
        if not created:
            liked.delete()
            status = 'unliked'
        else:
            status = 'liked'
        return JsonResponse({'status': status, 'likes_count': post.likes.count()})
    

    Follow System

    Following is a many‑to‑many relationship on the Profile model. Provide a view that adds or removes a follower.

    @login_required
    def follow_toggle(request, username):
        target = User.objects.get(username=username).profile
        profile = request.user.profile
        if target in profile.following.all():
            profile.following.remove(target)
            action = 'unfollowed'
        else:
            profile.following.add(target)
            action = 'followed'
        return JsonResponse({'action': action})
    

    Adding Real‑Time Features with Django Channels

    To deliver instant notifications (new likes, comments, or followers), integrate django‑channels and WebSockets.

    1. Install the package: pip install channels.
    2. Add 'channels' to INSTALLED_APPS and set ASGI_APPLICATION = 'socialapp.asgi.application'.
    3. Create a consumer that broadcasts events to a group named after the user’s ID.
    # consumers.py
    from channels.generic.websocket import AsyncJsonWebsocketConsumer
    
    class NotificationConsumer(AsyncJsonWebsocketConsumer):
        async def connect(self):
            if self.scope['user'].is_anonymous:
                await self.close()
            else:
                self.group_name = f'user_{self.scope["user"].id}'
                await self.channel_layer.group_add(self.group_name, self.channel_name)
                await self.accept()
    
        async def disconnect(self, close_code):
            await self.channel_layer.group_discard(self.group_name, self.channel_name)
    
        async def send_notification(self, event):
            await self.send_json(event['content'])
    

    Whenever a like or comment is saved, fire a channel layer event to push the notification instantly.

    Testing and Debugging

    • Unit Tests: Use django.test.TestCase to verify model constraints, view permissions, and API responses.
    • Integration Tests: Leverage pytest‑django for end‑to‑end scenarios, especially real‑time features.
    • Debug Toolbar: Install
  • Python Django Multi-Vendor E-Commerce Platform

    Building a Python Django multi‑vendor e‑commerce platform is one of the most rewarding challenges for modern web developers. It combines the power of Django’s “batteries‑included” philosophy with the flexibility needed to host dozens—or even thousands—of independent sellers under a single storefront. In this guide we’ll explore why Django is a natural fit for multi‑vendor marketplaces, outline the essential architecture, walk through a practical implementation roadmap, and share SEO‑friendly tips that keep your site visible in Google’s ever‑changing algorithm. Whether you’re a seasoned Django engineer or a startup founder looking for a scalable foundation, the insights below will help you launch a robust, secure, and search‑engine‑optimized marketplace faster than you imagined.

    Why Django Excels in Multi‑Vendor E‑Commerce

    Rapid Development with Built‑In Admin

    Django’s auto‑generated admin interface lets you manage products, vendors, orders, and payments without writing a single line of UI code. This accelerates MVP development and reduces the time‑to‑market for a multi‑vendor marketplace.

    Strong ORM for Complex Relationships

    Multi‑vendor platforms require many‑to‑many relationships (vendors ↔ products, products ↔ categories, orders ↔ items). Django’s Object‑Relational Mapping (ORM) handles these links cleanly, allowing you to write expressive queries like:

    vendors = Vendor.objects.filter(products__price__gt=100).distinct()
    

    Scalable Architecture Out‑of‑the‑Box

    With support for multiple databases, caching back‑ends, and asynchronous tasks via Celery, Django scales horizontally as your marketplace grows.

    Core Features of a Multi‑Vendor Marketplace

    • Vendor onboarding & dashboard – self‑service registration, profile management, and sales analytics.
    • Product catalog management – categories, attributes, inventory, and bulk import.
    • Order processing workflow – cart, checkout, split payments, and fulfillment notifications.
    • Commission & payout system – flexible fee structures and automated vendor payouts.
    • Review & rating engine – buyer feedback for both products and sellers.
    • Search & filtering – Elasticsearch or PostgreSQL full‑text search for fast product discovery.
    • SEO‑ready URLs & metadata – clean slugs, schema.org markup, and sitemap generation.
    • Security & compliance – GDPR, PCI‑DSS, and role‑based access control.

    High‑Level Architecture

    1. Presentation Layer

    Use django‑templates or a modern front‑end framework (React, Vue) served via Django’s TemplateView or DRF API endpoints.

    2. Business Logic Layer

    Separate concerns into reusable Django apps:

    • accounts – authentication, vendor profiles, permissions.
    • catalog – product models, categories, attribute handling.
    • orders – cart, checkout, order status, payment integration.
    • commissions – fee calculation, payout scheduling.
    • reviews – rating system, moderation workflow.

    3. Data Layer

    PostgreSQL is the preferred database for its JSONB support, full‑text search, and robust transactional guarantees. Use separate schemas or tablespaces for vendor‑specific data if needed.

    4. Asynchronous Tasks

    Integrate Celery with Redis or RabbitMQ to handle email notifications, inventory sync, and payout processing without blocking the request/response cycle.

    Essential Django Packages for Multi‑Vendor Marketplaces

    • django‑allauth – comprehensive authentication, social login, and email verification.
    • django‑mptt – efficient tree structures for category hierarchies.
    • django‑rest‑framework (DRF) – build RESTful APIs for mobile apps or SPA front‑ends.
    • django‑stripe or dj‑paypal – payment gateway integrations.
    • django‑sitetree – dynamic navigation menus based on vendor categories.
    • django‑sorl‑thumbnail – image resizing and optimization for product photos.
    • django‑compressor – CSS/JS minification for faster page loads.

    Step‑by‑Step Implementation Guide

    1. Set up the Django project
      django-admin startproject marketplace
      cd marketplace
      python -m venv venv
      source venv/bin/activate
      pip install django djangorestframework celery redis psycopg2-binary
      
    2. Create core apps
      python manage.py startapp accounts
      python manage.py startapp catalog
      python manage.py startapp orders
      python manage.py startapp commissions
      python manage.py startapp reviews
      
    3. Define the Vendor model
      # accounts/models.py
      from django.contrib.auth.models import AbstractUser
      from django.db import models
      
      class Vendor(AbstractUser):
          store_name = models.CharField(max_length=255, unique=True)
          bio = models.TextField(blank=True)
          logo = models.ImageField(upload_to='vendor_logos/', null=True, blank=True)
      
          class Meta:
              verbose_name = 'Vendor'
              verbose_name_plural = 'Vendors'
      
    4. Link products to vendors
      # catalog/models.py
      class Product(models.Model):
          vendor = models.ForeignKey('accounts.Vendor', on_delete=models.CASCADE, related_name='products')
          name = models.CharField(max_length=200)
          slug = models.SlugField(unique=True)
          description = models.TextField()
          price = models.DecimalField(max_digits=10, decimal_places=2)
          stock = models.PositiveIntegerField()
          created_at = models.DateTimeField(auto_now_add=True)
      
          def __str__(self):
              return self.name
      
    5. Implement split‑payment logic
      # commissions/services.py
      from decimal import Decimal
      from .models import Order, Commission
      
      def calculate_commission(order):
          platform_fee = Decimal('0.10')  # 10%
          for item in order.items.all():
              vendor_share = item.price * (1 - platform_fee)
              Commission.objects.create(
                  order=order,
                  vendor=item.product.vendor,
                  amount=vendor_share,
                  fee=item.price * platform_fee,
              )
      
    6. Configure Celery for async tasks
      # marketplace/celery.py
      import os
      from celery import Celery
      
      os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'marketplace.settings')
      app = Celery('marketplace')
      app.config_from_object('django.conf:settings', namespace='CELERY')
      app.autodiscover_tasks()
      
    7. Add SEO‑friendly URLs and sitemaps
      # catalog/urls.py
      from django.urls import path
      from . import views
      
      urlpatterns = [
          path('store//', views.StoreDetailView.as_view(), name='store-detail'),
          path('product//', views.ProductDetailView.as_view(), name='product-detail'),
      ]
      

      Use django‑sitemap to generate XML sitemaps automatically.

    8. Deploy with Docker & Gunicorn
      # Dockerfile
      FROM python:3.12-slim
      WORKDIR /app
      COPY requirements.txt .
      RUN pip install -r requirements.txt
      COPY . .
      CMD ["gunicorn", "marketplace.wsgi:application", "--bind", "0.0.0.0:8000"]
      

    Performance & Scaling Tips

    • Cache frequently accessed data – product listings, category trees, and vendor dashboards benefit from Redis or Memcached.
    • Use database indexing on fields like slug, vendor_id, and price to speed up filter queries.
    • Implement pagination on catalog pages to reduce payload size.
    • Offload static & media files to a CDN (Amazon S3 + CloudFront) for faster delivery.
    • Adopt read‑replicas for PostgreSQL to separate heavy read traffic from write operations.

    Security Best Practices

    1. Enable SECURE_SSL_REDIRECT and SESSION_COOKIE_SECURE in settings.py.
    2. Use Django’s built‑in CSRF protection on all forms and API endpoints.
  • Python Django Custom User Model Guide

    Creating a robust authentication system is often the first step when building a Django application. While Django’s default User model works for many projects, real‑world apps frequently need extra fields, alternative login methods, or stricter validation. That’s where a custom user model shines. In this comprehensive guide we’ll walk you through why you should replace the default model, how to design and implement a custom user model, and the best practices that keep your code clean, secure, and future‑proof.

    Why Use a Custom User Model?

    • Flexibility: Add fields such as date_of_birth, profile_picture, or is_premium without creating a separate profile model.
    • Unified authentication: Use email, phone number, or a third‑party identifier as the primary login credential.
    • Scalability: Avoid the pain of migrating data later; switching after the first migration can be complex and risky.
    • Compliance: Meet GDPR or industry‑specific requirements by storing only the data you truly need.

    When to Create a Custom User Model

    Ideally, you should decide on a custom user model before running the first migration. If you’ve already created tables with the default model, you can still migrate, but the process is more involved and may require data export/import.

    Step‑by‑Step Guide to Building a Custom User Model

    1. Plan Your Fields and Authentication Method

    Start by listing the attributes your users need. Common choices include:

    email (unique)
    first_name
    last_name
    date_of_birth
    is_staff
    is_active
    is_premium
    

    If you want users to log in with their email instead of a username, you’ll need to set USERNAME_FIELD = 'email' later in the model.

    2. Create a New App (Optional but Recommended)

    Keeping authentication logic isolated makes maintenance easier.

    python manage.py startapp accounts
    

    3. Define the Custom User Model

    In accounts/models.py, subclass AbstractBaseUser and PermissionsMixin. The latter provides group and permission handling out of the box.

    from django.contrib.auth.base_user import AbstractBaseUser
    from django.contrib.auth.models import PermissionsMixin
    from django.db import models
    from django.utils import timezone
    from django.core.mail import send_mail
    from .managers import CustomUserManager
    
    class CustomUser(AbstractBaseUser, PermissionsMixin):
        email = models.EmailField('email address', unique=True)
        first_name = models.CharField('first name', max_length=30, blank=True)
        last_name = models.CharField('last name', max_length=30, blank=True)
        date_of_birth = models.DateField('date of birth', null=True, blank=True)
        is_staff = models.BooleanField(
            'staff status',
            default=False,
            help_text='Designates whether the user can log into this admin site.'
        )
        is_active = models.BooleanField(
            'active',
            default=True,
            help_text='Designates whether this user should be treated as active.'
        )
        date_joined = models.DateTimeField('date joined', default=timezone.now)
    
        objects = CustomUserManager()
    
        USERNAME_FIELD = 'email'
        REQUIRED_FIELDS = []  # Email & password are required by default.
    
        class Meta:
            verbose_name = 'user'
            verbose_name_plural = 'users'
    
        def __str__(self):
            return self.email
    
        def get_full_name(self):
            return f"{self.first_name} {self.last_name}".strip()
    
        def get_short_name(self):
            return self.first_name or self.email
    
        def email_user(self, subject, message, from_email=None, **kwargs):
            send_mail(subject, message, from_email, [self.email], **kwargs)
    

    4. Implement a Custom User Manager

    The manager handles user creation, especially for the createsuperuser command.

    from django.contrib.auth.base_user import BaseUserManager
    
    class CustomUserManager(BaseUserManager):
        use_in_migrations = True
    
        def _create_user(self, email, password, **extra_fields):
            if not email:
                raise ValueError('The Email field must be set')
            email = self.normalize_email(email)
            user = self.model(email=email, **extra_fields)
            user.set_password(password)
            user.save(using=self._db)
            return user
    
        def create_user(self, email, password=None, **extra_fields):
            extra_fields.setdefault('is_staff', False)
            extra_fields.setdefault('is_superuser', False)
            return self._create_user(email, password, **extra_fields)
    
        def create_superuser(self, email, password, **extra_fields):
            extra_fields.setdefault('is_staff', True)
            extra_fields.setdefault('is_superuser', True)
    
            if extra_fields.get('is_staff') is not True:
                raise ValueError('Superuser must have is_staff=True.')
            if extra_fields.get('is_superuser') is not True:
                raise ValueError('Superuser must have is_superuser=True.')
    
            return self._create_user(email, password, **extra_fields)
    

    5. Tell Django to Use the New Model

    Add the following line to your project’s settings.py:

    AUTH_USER_MODEL = 'accounts.CustomUser'
    

    Make sure the accounts app is listed before django.contrib.auth in INSTALLED_APPS to avoid circular import issues.

    6. Create and Apply Migrations

    python manage.py makemigrations accounts
    python manage.py migrate
    

    If you’re starting a new project, Django will create the auth_user table based on your custom model, and everything will line up automatically.

    7. Update Admin Interface

    Register the custom user model with the admin site so you can manage users easily.

    from django.contrib import admin
    from django.contrib.auth.admin import UserAdmin
    from .models import CustomUser
    from .forms import CustomUserCreationForm, CustomUserChangeForm
    
    class CustomUserAdmin(UserAdmin):
        add_form = CustomUserCreationForm
        form = CustomUserChangeForm
        model = CustomUser
        list_display = ('email', 'first_name', 'last_name', 'is_staff', 'is_active')
        list_filter = ('is_staff', 'is_active')
        ordering = ('email',)
        search_fields = ('email', 'first_name', 'last_name')
        fieldsets = (
            (None, {'fields': ('email', 'password')}),
            ('Personal info', {'fields': ('first_name', 'last_name', 'date_of_birth')}),
            ('Permissions', {'fields': ('is_staff', 'is_active', 'is_superuser', 'groups', 'user_permissions')}),
            ('Important dates', {'fields': ('last_login', 'date_joined')}),
        )
        add_fieldsets = (
            (None, {
                'classes': ('wide',),
                'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active')}
            ),
        )
    
    admin.site.register(CustomUser, CustomUserAdmin)
    

    8. Create Custom Forms for Admin and Registration

    These forms ensure proper validation for the new fields.

    from django import forms
    from django.contrib.auth.forms import UserCreationForm, UserChangeForm
    from .models import CustomUser
    
    class CustomUserCreationForm(UserCreationForm):
        class Meta(UserCreationForm.Meta):
            model = CustomUser
            fields = ('email', 'first_name', 'last_name', 'date_of_birth')
    
    class CustomUserChangeForm(UserChangeForm):
        class Meta:
            model = CustomUser
            fields = ('email', 'first_name', 'last_name', 'date_of_birth', 'is_active', 'is_staff')
    

    Testing Your Custom User Model

    Write a few simple tests to verify that user creation, authentication, and admin integration work as expected.

    from django.test import TestCase
    from django.contrib.auth import get_user_model
    
    class UserModelTests(TestCase):
        def test_create_user_with_email(self):
            User = get_user_model()
            user = User.objects.create_user(email='test@example.com', password='test123')
            self.assertEqual(user.email, 'test@example.com')
            self.assertTrue(user.check_password('test123'))
            self.assertFalse(user.is_staff)
    
        def test_create_superuser(self):
            User = get_user_model()
            admin = User.objects.create_superuser(email='admin@example.com', password='admin123')
            self.assertTrue(admin.is_staff)
            self.assertTrue(admin.is_superuser)
    

    Common Pitfalls & How to Avoid Them

    • Changing the model after migrations: If you need to add a field later, use makemigrations as usual, but avoid altering USERNAME_FIELD after the initial migration.
    • Forgot to update AUTH_USER_MODEL: Django will still reference the default auth.User, leading to obscure errors.
    • Using django.contrib.auth.forms.AuthenticationForm without customization: It expects a username field. Replace it with a custom form that authenticates via email.
    • Hard‑coding user lookups: Always use get_user_model() instead of importing the model directly.

    Best Practices for a Clean, Maintainable Custom User Model

    1. Define the model early: Decide on the user schema before any data exists.
    2. Keep authentication logic separate: Use a dedicated accounts app for models, managers, forms, and views.
    3. Leverage Django’s built‑in mixins: PermissionsMixin adds groups and permission handling without extra code.
  • Python Django Rest Framework Drf Tutorial

    Welcome to the ultimate Python Django REST Framework (DRF) tutorial! Whether you’re a seasoned Django developer or just dipping your toes into API development, this guide will walk you through every essential step—from setting up your environment to building a production‑ready RESTful API. By the end, you’ll have a solid grasp of DRF’s core components, best practices, and how to deliver scalable, secure APIs that search engines love. Let’s dive in and transform your Django projects into powerful data‑driven services.

    What Is Django REST Framework?

    Django REST Framework is a flexible, modular toolkit for building Web APIs on top of the Django ecosystem. It abstracts away much of the boilerplate involved in serialization, request handling, and authentication, letting you focus on business logic. DRF is widely adopted because it integrates seamlessly with Django’s ORM, supports both function‑based and class‑based views, and provides a browsable API that’s perfect for debugging and documentation.

    Core Concepts You Need to Know

    • Serializers: Convert complex Django model instances into native Python data types (and vice‑versa) for JSON, XML, or other formats.
    • ViewSets: Combine the logic for a set of related views (list, retrieve, create, update, delete) into a single class.
    • Routers: Automatically generate URL patterns for ViewSets, reducing manual routing work.
    • Authentication & Permissions: Control who can access your API and what actions they can perform.
    • Browsable API: An HTML interface that lets developers explore endpoints directly from a web browser.

    Setting Up the Development Environment

    Prerequisites

    Before you start, make sure you have the following installed:

    • Python 3.9+ (preferably via pyenv or a virtual environment)
    • pip (Python package installer)
    • Git (optional, but recommended for version control)
    • A code editor such as VS Code or PyCharm

    Installing Django and DRF

    # Create and activate a virtual environment
    python -m venv env
    source env/bin/activate   # On Windows use `env\Scripts\activate`
    
    # Upgrade pip and install Django with DRF
    pip install --upgrade pip
    pip install django djangorestframework
    

    Once installed, verify the versions:

    python -c "import django, rest_framework; print('Django', django.get_version(), 'DRF', rest_framework.__version__)"
    

    Building Your First API

    1. Create a Django Project

    django-admin startproject myproject
    cd myproject
    

    2. Create an App

    python manage.py startapp blog
    

    Don’t forget to add 'blog' and 'rest_framework' to INSTALLED_APPS in myproject/settings.py:

    INSTALLED_APPS = [
        # default Django apps …
        'rest_framework',
        'blog',
    ]
    

    3. Define a Model

    In blog/models.py create a simple Post model:

    from django.db import models
    
    class Post(models.Model):
        title = models.CharField(max_length=200)
        content = models.TextField()
        created_at = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return self.title
    

    4. Create a Serializer

    Serializers translate model instances to JSON. Add blog/serializers.py:

    from rest_framework import serializers
    from .models import Post
    
    class PostSerializer(serializers.ModelSerializer):
        class Meta:
            model = Post
            fields = ['id', 'title', 'content', 'created_at']
    

    5. Build Views with ViewSets

    In blog/views.py use a ModelViewSet to get CRUD operations for free:

    from rest_framework import viewsets
    from .models import Post
    from .serializers import PostSerializer
    
    class PostViewSet(viewsets.ModelViewSet):
        queryset = Post.objects.all().order_by('-created_at')
        serializer_class = PostSerializer
    

    6. Register Routes with Routers

    DRF routers automatically generate the URL conf. Edit myproject/urls.py:

    from django.contrib import admin
    from django.urls import path, include
    from rest_framework import routers
    from blog.views import PostViewSet
    
    router = routers.DefaultRouter()
    router.register(r'posts', PostViewSet)
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/', include(router.urls)),
        path('api-auth/', include('rest_framework.urls')),  # optional login UI
    ]
    

    7. Migrate and Test

    python manage.py makemigrations
    python manage.py migrate
    python manage.py runserver
    

    Visit http://127.0.0.1:8000/api/posts/ in your browser. You’ll see the DRF browsable API, allowing you to list, create, and edit posts without writing any extra HTML.

    Handling Authentication and Permissions

    Securing an API is as important as building it. DRF provides several out‑of‑the‑box authentication schemes and a flexible permission system.

    Common Authentication Methods

    • SessionAuthentication: Uses Django’s session framework; great for web‑based clients.
    • TokenAuthentication: Issues a simple token per user; ideal for mobile apps.
    • JWTAuthentication: JSON Web Tokens provide stateless, scalable auth (requires djangorestframework-simplejwt).

    Setting Up Token Authentication

    # Install the token package
    pip install djangorestframework-authtoken
    
    # Add to INSTALLED_APPS
    INSTALLED_APPS += ['rest_framework.authtoken']
    
    # Migrate
    python manage.py migrate
    
    # Update REST_FRAMEWORK settings (myproject/settings.py)
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework.authentication.TokenAuthentication',
        ],
        'DEFAULT_PERMISSION_CLASSES': [
            'rest_framework.permissions.IsAuthenticated',
        ],
    }
    

    Create a token for a user via the admin or the command line:

    python manage.py drf_create_token your_username
    

    Permission Classes You’ll Use Frequently

    • AllowAny – Open to all (useful for public read‑only endpoints).
    • IsAuthenticated – Requires a valid user.
    • IsAdminUser – Restricts access to staff/superusers.
    • IsOwnerOrReadOnly – Custom class that lets owners edit, others only read.

    Testing Your API

    Automated testing ensures your API behaves as expected. DRF extends Django’s test client with JSON support.

    from rest_framework.test import APITestCase
    from django.urls import reverse
    from .models import Post
    from django.contrib.auth.models import User
    
    class PostAPITest(APITestCase):
        def setUp(self):
            self.user = User.objects.create_user(username='tester', password='pass')
            self.client.login(username='tester', password='pass')
            self.post = Post.objects.create(title='First', content='Hello World')
    
        def test_list_posts(self):
            url = reverse('post-list')  # auto‑generated by router
            response = self.client.get(url, format='json')
            self.assertEqual(response.status_code, 200)
            self.assertEqual(len(response.data), 1)
    
        def test_create_post(self):
            url = reverse('post-list')
            data = {'title': 'New Post', 'content': 'Content here'}
            response = self.client.post(url, data, format='json')
            self.assertEqual(response.status_code, 201)
            self.assertEqual(Post.objects.count(), 2)
    

    Common Pitfalls and Best Practices

    • Never expose raw model fields without validation—use serializers to enforce constraints.
    • Keep your views.py thin; business logic belongs in services or model methods.
    • Leverage select_related and prefetch_related in querysets to avoid N+1 query problems.
    • Document your API with
  • Python Flask E-Commerce Cart Management

    Building a robust e‑commerce platform with Python Flask starts with one of the most critical components: the shopping cart. A well‑designed cart not only boosts conversion rates but also provides a seamless user experience that keeps customers coming back. In this guide, we’ll walk through the essential concepts, best practices, and hands‑on code snippets you need to master Flask e‑commerce cart management. Whether you’re a seasoned developer or just getting started with Flask, you’ll discover actionable steps to create a secure, scalable, and SEO‑friendly cart system.

    Why Cart Management Is a Cornerstone of E‑Commerce Success

    Search engines love sites that offer a smooth buying journey. A cart that loads quickly, remembers items across sessions, and integrates cleanly with checkout can improve user dwell time and reduce bounce rates—both key signals for SEO. Moreover, a reliable cart reduces cart abandonment, which directly translates into higher revenue.

    Core Architecture of a Flask Shopping Cart

    Before diving into code, let’s outline the typical layers involved in a Flask cart system:

    • Front‑end UI: HTML templates, JavaScript (often with AJAX), and CSS for a responsive design.
    • Flask routes & views: Endpoints that add, update, remove, and display cart items.
    • Session storage: Temporary storage (Flask session) for guest users.
    • Database persistence: SQLAlchemy models to save carts for logged‑in users.
    • Business logic: Validation, stock checks, price calculations, and discount handling.

    Choosing the Right Storage Strategy

    Two common approaches exist:

    1. Session‑based carts: Ideal for guests; data lives in signed cookies or server‑side Redis.
    2. Database‑backed carts: Required for registered users; ensures cart items survive across devices.

    Most production sites combine both: store items in the session for anonymous visitors, then migrate them to the database once the user logs in.

    Setting Up the Project Structure

    Here’s a clean layout that scales well:

    myshop/
    │
    ├─ app/
    │   ├─ __init__.py
    │   ├─ models.py
    │   ├─ routes.py
    │   ├─ cart.py          # cart‑related business logic
    │   └─ templates/
    │        └─ cart.html
    │
    ├─ migrations/
    ├─ static/
    │   └─ js/cart.js
    └─ run.py
    

    Implementing the Cart Model with SQLAlchemy

    Even if you start with a session‑only cart, defining a model early saves refactoring later.

    from datetime import datetime
    from flask_sqlalchemy import SQLAlchemy
    
    db = SQLAlchemy()
    
    class Cart(db.Model):
        __tablename__ = 'carts'
        id = db.Column(db.Integer, primary_key=True)
        user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
        created_at = db.Column(db.DateTime, default=datetime.utcnow)
    
        items = db.relationship('CartItem', backref='cart', lazy='dynamic')
    
    class CartItem(db.Model):
        __tablename__ = 'cart_items'
        id = db.Column(db.Integer, primary_key=True)
        cart_id = db.Column(db.Integer, db.ForeignKey('carts.id'))
        product_id = db.Column(db.Integer, db.ForeignKey('products.id'))
        quantity = db.Column(db.Integer, default=1)
        price_at_add = db.Column(db.Numeric(10, 2))
    
        def subtotal(self):
            return self.quantity * self.price_at_add
    

    Session‑Based Cart Helper Functions

    These utilities keep the session payload lightweight while providing a dictionary‑like interface.

    from flask import session, current_app
    import json
    
    def _load_cart():
        return session.get('cart', {})
    
    def _save_cart(cart):
        session['cart'] = cart
        session.modified = True
    
    def add_to_cart(product_id, quantity=1):
        cart = _load_cart()
        product_key = str(product_id)
        if product_key in cart:
            cart[product_key] += quantity
        else:
            cart[product_key] = quantity
        _save_cart(cart)
    
    def remove_from_cart(product_id):
        cart = _load_cart()
        product_key = str(product_id)
        cart.pop(product_key, None)
        _save_cart(cart)
    
    def clear_cart():
        session.pop('cart', None)
        session.modified = True
    

    Flask Routes for Cart Operations

    Below are the essential endpoints. Notice the use of @login_required from Flask‑Login to protect user‑specific actions.

    from flask import Blueprint, render_template, request, jsonify, redirect, url_for
    from flask_login import login_required, current_user
    from .cart import add_to_cart, remove_from_cart, clear_cart, _load_cart
    from .models import db, Cart, CartItem, Product
    
    bp = Blueprint('cart', __name__, url_prefix='/cart')
    
    @bp.route('/')
    def view_cart():
        cart = _load_cart()
        # Convert session data to product objects for display
        product_ids = [int(pid) for pid in cart.keys()]
        products = Product.query.filter(Product.id.in_(product_ids)).all()
        items = []
        for p in products:
            qty = cart[str(p.id)]
            items.append({'product': p, 'quantity': qty, 'subtotal': p.price * qty})
        return render_template('cart.html', items=items)
    
    @bp.route('/add', methods=['POST'])
    def add():
        data = request.get_json()
        add_to_cart(data['product_id'], data.get('quantity', 1))
        return jsonify({'status': 'success', 'message': 'Item added to cart'})
    
    @bp.route('/remove', methods=['POST'])
    def remove():
        data = request.get_json()
        remove_from_cart(data['product_id'])
        return jsonify({'status': 'success', 'message': 'Item removed from cart'})
    
    @bp.route('/clear', methods=['POST'])
    @login_required
    def clear():
        # If user is logged in, also clear DB cart
        db_cart = Cart.query.filter_by(user_id=current_user.id).first()
        if db_cart:
            CartItem.query.filter_by(cart_id=db_cart.id).delete()
            db.session.commit()
        clear_cart()
        return jsonify({'status': 'success', 'message': 'Cart cleared'})
    

    Migrating Session Cart to Database on Login

    When a guest logs in, we want to preserve their selections. The following signal handler does exactly that:

    from flask_login import user_logged_in
    from .cart import _load_cart, clear_cart
    from .models import db, Cart, CartItem, Product
    
    @user_logged_in.connect_via(app)
    def merge_carts(sender, user):
        session_cart = _load_cart()
        if not session_cart:
            return
    
        # Find or create a persistent cart for the user
        db_cart = Cart.query.filter_by(user_id=user.id).first()
        if not db_cart:
            db_cart = Cart(user_id=user.id)
            db.session.add(db_cart)
            db.session.flush()  # obtain db_cart.id
    
        for pid, qty in session_cart.items():
            product = Product.query.get(int(pid))
            if not product:
                continue
            existing_item = CartItem.query.filter_by(cart_id=db_cart.id,
                                                     product_id=product.id).first()
            if existing_item:
                existing_item.quantity += qty
            else:
                new_item = CartItem(
                    cart_id=db_cart.id,
                    product_id=product.id,
                    quantity=qty,
                    price_at_add=product.price
                )
                db.session.add(new_item)
    
        db.session.commit()
        clear_cart()  # Remove temporary session data
    

    Front‑End Integration with AJAX

    Modern shoppers expect instant feedback. Using JavaScript’s fetch API, you can add items without a full page reload.

    // static/js/cart.js
    function addToCart(productId, quantity = 1) {
        fetch('/cart/add', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({product_id: productId, quantity: quantity})
        })
        .then(res => res.json())
        .then(data => {
            if (data.status === 'success') {
                updateCartBadge();
                alert('Item added to cart!');
            }
        });
    }
    
    function updateCartBadge() {
        fetch('/cart/count')
            .then(res => res.json())
            .then(data => {
                document.getElementById('cart-count').textContent = data.count;
            });
    }
    

    SEO‑Friendly Cart Pages

    Search engines can index cart pages, but you should avoid duplicate content and ensure fast load times. Follow these best practices:

    • Canonical URLs: Use <link rel="canonical"> to point to the main cart URL.
    • Structured data: Add Schema.org Product and Offer markup for each cart item.
    • Lazy loading: Load product images asynchronously to improve LCP (Largest Contentful Paint).
    • Minify assets: Compress CSS/JS and enable GZIP on the Flask server.

    Example of Structured Data for a Cart Item

    <script type="application/ld+json">
    {
    "@context": "https://schema.org",
    "@type": "Offer",
    "itemOffered": {
    "@type": "Product