You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django Oscar 1.5及后续版本中实现多租户(Multitenancy)?

Hey there! I've worked on setting up multi-tenancy in Django Oscar 1.5+ before, since the old 0.6 multi-dealer feature was removed. Let me walk you through the most practical approaches to make this work:

1. Row-Level Isolation (Most Common & Flexible)

This approach adds a tenant identifier to core Oscar models and filters data automatically, so each shop owner only sees their own content.

  • Step 1: Add a Tenant Model & Link to Oscar Core Models
    First, create a Shop model to represent each tenant (shop owner), then extend Oscar's key models (like Product, Partner, Order) with a foreign key to Shop. Use Oscar's model overriding mechanism to avoid modifying core code:

    # myshop/shops/models.py
    from django.db import models
    from django.contrib.auth.models import User
    from oscar.apps.catalogue.abstract_models import AbstractProduct
    from oscar.apps.partner.abstract_models import AbstractPartner
    
    class Shop(models.Model):
        name = models.CharField(max_length=255, unique=True)
        owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="shops")
        # Add fields like domain, logo, or custom settings here
    
    # Override Oscar's Product model
    class Product(AbstractProduct):
        shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
    
    # Override Oscar's Partner model
    class Partner(AbstractPartner):
        shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
    
    # Don't forget to import Oscar's abstract models at the end
    from oscar.apps.catalogue.models import *
    from oscar.apps.partner.models import *
    

    Update your INSTALLED_APPS to replace Oscar's default apps with your overridden versions.

  • Step 2: Auto-Filter Data with Custom Managers
    Create a tenant-aware manager that automatically filters queries to the current shop:

    # myshop/core/managers.py
    from django.db import models
    
    class TenantManager(models.Manager):
        def get_queryset(self):
            qs = super().get_queryset()
            # Access the current shop from the request (set via middleware)
            if hasattr(self.model, '_current_shop'):
                return qs.filter(shop=self.model._current_shop)
            return qs
    
    # Attach this manager to your overridden models
    # e.g., in Product:
    # objects = TenantManager()
    

    You'll need a way to set _current_shop on models, which ties into the next step.

  • Step 3: Identify Tenants with Middleware
    Add middleware to detect the current shop (via subdomain, URL path, or user session) and attach it to the request:

    # myshop/core/middleware.py
    class TenantMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            # Example: Detect shop via subdomain (e.g., shop1.yourdomain.com)
            host_parts = request.get_host().split('.')
            if len(host_parts) >= 3:
                shop_slug = host_parts[0]
                try:
                    request.shop = Shop.objects.get(name=shop_slug)
                    # Set the current shop for tenant-aware managers
                    from myshop.shops.models import Product, Partner
                    Product._current_shop = request.shop
                    Partner._current_shop = request.shop
                except Shop.DoesNotExist:
                    # Handle invalid shop (e.g., redirect to 404 or default page)
                    pass
            response = self.get_response(request)
            return response
    

    Add this middleware to settings.MIDDLEWARE (place it early in the stack).

  • Step 4: Enforce Permissions
    Ensure only the shop owner can modify their shop's data with a custom decorator:

    # myshop/core/decorators.py
    from django.core.exceptions import PermissionDenied
    
    def shop_owner_required(view_func):
        def wrapper(request, *args, **kwargs):
            if not hasattr(request, 'shop') or request.user != request.shop.owner:
                raise PermissionDenied
            return view_func(request, *args, **kwargs)
        return wrapper
    

    Apply this decorator to your shop management views.

2. Database Schema Isolation (For Strict Separation)

If you need complete data isolation between shops (no shared tables), use a multi-tenancy library like django-tenants (PostgreSQL-only, since it uses database schemas).

  • Key Steps:

    • Install django-tenants and configure your settings to use a tenant-aware database router.
    • Set your Shop model as the tenant model.
    • Migrate Oscar's models into tenant schemas (instead of the public schema).
    • Use the library's middleware to switch schemas based on the current shop.

    This approach is more heavyweight but ensures zero data leakage between tenants.

3. Django Sites Framework (Simple, Limited Isolation)

For basic multi-shop setups where some data (e.g., categories) can be shared, use Django's built-in sites framework:

  • Add a site foreign key to Oscar models (similar to the Shop field in approach 1).
  • Filter queries using Site.objects.get_current() to return only data for the current site.
  • Use Django's sites middleware to detect the current site via domain.

This is simpler but less flexible than row-level isolation, as it's designed for shared content across domains.

Critical Notes
  • Oscar Overrides: Always use Oscar's model/app overriding system instead of modifying core code—this ensures compatibility with future updates.
  • Search: If using Haystack or Elasticsearch, update your search indexes to include the shop/site field and filter results accordingly.
  • Media/Static Files: Store tenant-specific media (like product images) in separate directories (e.g., media/shop1/) to avoid conflicts.

内容的提问来源于stack exchange,提问作者kurtgn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:13:22