Исправлена ошибка public admin для мультитенантной архитектуры
Проблема: при входе в localhost/admin/ (public схема) возникала ошибка "relation user_roles_userrole does not exist", так как tenant-only таблицы не существуют в public схеме. Решение: - Создан TenantAdminOnlyMixin для скрытия tenant-only моделей от public admin - Применён миксин ко всем ModelAdmin классам в tenant-only приложениях: user_roles, customers, orders, inventory, products - Добавлена проверка _is_public_schema() в RoleBasedPermissionBackend для предотвращения запросов к tenant-only таблицам в public схеме Теперь: - localhost/admin/ показывает только public модели (Client, Domain, User) - shop.localhost/admin/ показывает все модели магазина 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
"""
|
||||
Админка для приложения inventory.
|
||||
Все модели tenant-only, поэтому используют TenantAdminOnlyMixin
|
||||
для скрытия от public admin (localhost/admin/).
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.utils.html import format_html
|
||||
from django.urls import reverse
|
||||
@@ -11,11 +16,12 @@ from inventory.models import (
|
||||
IncomingDocument, IncomingDocumentItem, Transformation, TransformationInput,
|
||||
TransformationOutput, TransferDocument, TransferDocumentItem
|
||||
)
|
||||
from tenants.admin_mixins import TenantAdminOnlyMixin
|
||||
|
||||
|
||||
# ===== SHOWCASE =====
|
||||
@admin.register(Showcase)
|
||||
class ShowcaseAdmin(admin.ModelAdmin):
|
||||
class ShowcaseAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('name', 'warehouse', 'is_default', 'is_active', 'created_at')
|
||||
list_filter = ('is_active', 'is_default', 'warehouse', 'created_at')
|
||||
search_fields = ('name', 'warehouse__name')
|
||||
@@ -34,7 +40,7 @@ class ShowcaseAdmin(admin.ModelAdmin):
|
||||
|
||||
# ===== WAREHOUSE =====
|
||||
@admin.register(Warehouse)
|
||||
class WarehouseAdmin(admin.ModelAdmin):
|
||||
class WarehouseAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('name', 'is_default_display', 'is_active', 'created_at')
|
||||
list_filter = ('is_active', 'is_default', 'created_at')
|
||||
search_fields = ('name',)
|
||||
@@ -58,7 +64,7 @@ class WarehouseAdmin(admin.ModelAdmin):
|
||||
|
||||
# ===== STOCK BATCH =====
|
||||
@admin.register(StockBatch)
|
||||
class StockBatchAdmin(admin.ModelAdmin):
|
||||
class StockBatchAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('product', 'warehouse', 'quantity_display', 'cost_price', 'created_at', 'is_active')
|
||||
list_filter = ('warehouse', 'is_active', 'created_at')
|
||||
search_fields = ('product__name', 'product__sku', 'warehouse__name')
|
||||
@@ -104,7 +110,7 @@ class SaleBatchAllocationInline(admin.TabularInline):
|
||||
|
||||
# ===== SALE =====
|
||||
@admin.register(Sale)
|
||||
class SaleAdmin(admin.ModelAdmin):
|
||||
class SaleAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('product', 'warehouse', 'quantity', 'sale_price', 'order_display', 'processed_display', 'date')
|
||||
list_filter = ('warehouse', 'processed', 'date')
|
||||
search_fields = ('product__name', 'order__order_number')
|
||||
@@ -142,7 +148,7 @@ class SaleAdmin(admin.ModelAdmin):
|
||||
|
||||
# ===== WRITE OFF =====
|
||||
@admin.register(WriteOff)
|
||||
class WriteOffAdmin(admin.ModelAdmin):
|
||||
class WriteOffAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('batch', 'quantity', 'reason_display', 'cost_price', 'date')
|
||||
list_filter = ('reason', 'date', 'batch__warehouse')
|
||||
search_fields = ('batch__product__name', 'document_number')
|
||||
@@ -176,7 +182,7 @@ class InventoryLineInline(admin.TabularInline):
|
||||
|
||||
# ===== INVENTORY =====
|
||||
@admin.register(Inventory)
|
||||
class InventoryAdmin(admin.ModelAdmin):
|
||||
class InventoryAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('warehouse', 'status_display', 'date', 'conducted_by')
|
||||
list_filter = ('status', 'date', 'warehouse')
|
||||
search_fields = ('warehouse__name', 'conducted_by')
|
||||
@@ -234,7 +240,7 @@ class InventoryAdmin(admin.ModelAdmin):
|
||||
|
||||
# ===== RESERVATION =====
|
||||
@admin.register(Reservation)
|
||||
class ReservationAdmin(admin.ModelAdmin):
|
||||
class ReservationAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('product', 'warehouse', 'quantity', 'status_display', 'context_info', 'reserved_at')
|
||||
list_filter = ('status', 'reserved_at', 'warehouse', 'showcase')
|
||||
search_fields = ('product__name', 'order_item__order__order_number', 'showcase__name')
|
||||
@@ -279,7 +285,7 @@ class ReservationAdmin(admin.ModelAdmin):
|
||||
|
||||
# ===== STOCK =====
|
||||
@admin.register(Stock)
|
||||
class StockAdmin(admin.ModelAdmin):
|
||||
class StockAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('product', 'warehouse', 'quantity_available', 'quantity_reserved', 'quantity_free', 'updated_at')
|
||||
list_filter = ('warehouse', 'updated_at')
|
||||
search_fields = ('product__name', 'product__sku', 'warehouse__name')
|
||||
@@ -305,7 +311,7 @@ class WriteOffDocumentItemInline(admin.TabularInline):
|
||||
|
||||
|
||||
@admin.register(WriteOffDocument)
|
||||
class WriteOffDocumentAdmin(admin.ModelAdmin):
|
||||
class WriteOffDocumentAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('document_number', 'warehouse', 'status_display', 'date', 'items_count', 'total_quantity_display', 'created_by', 'created_at')
|
||||
list_filter = ('status', 'warehouse', 'date', 'created_at')
|
||||
search_fields = ('document_number', 'warehouse__name')
|
||||
@@ -346,7 +352,7 @@ class WriteOffDocumentAdmin(admin.ModelAdmin):
|
||||
|
||||
|
||||
@admin.register(WriteOffDocumentItem)
|
||||
class WriteOffDocumentItemAdmin(admin.ModelAdmin):
|
||||
class WriteOffDocumentItemAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('document', 'product', 'quantity', 'reason', 'created_at')
|
||||
list_filter = ('reason', 'document__status', 'created_at')
|
||||
search_fields = ('product__name', 'document__document_number')
|
||||
@@ -362,7 +368,7 @@ class IncomingDocumentItemInline(admin.TabularInline):
|
||||
|
||||
|
||||
@admin.register(IncomingDocument)
|
||||
class IncomingDocumentAdmin(admin.ModelAdmin):
|
||||
class IncomingDocumentAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('document_number', 'warehouse', 'status_display', 'receipt_type_display', 'date', 'items_count', 'total_quantity_display', 'created_by', 'created_at')
|
||||
list_filter = ('status', 'warehouse', 'receipt_type', 'date', 'created_at')
|
||||
search_fields = ('document_number', 'warehouse__name', 'supplier_name')
|
||||
@@ -416,7 +422,7 @@ class IncomingDocumentAdmin(admin.ModelAdmin):
|
||||
|
||||
|
||||
@admin.register(IncomingDocumentItem)
|
||||
class IncomingDocumentItemAdmin(admin.ModelAdmin):
|
||||
class IncomingDocumentItemAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ('document', 'product', 'quantity', 'cost_price', 'total_cost_display', 'created_at')
|
||||
list_filter = ('document__status', 'document__receipt_type', 'created_at')
|
||||
search_fields = ('product__name', 'document__document_number')
|
||||
@@ -445,7 +451,7 @@ class TransformationOutputInline(admin.TabularInline):
|
||||
|
||||
|
||||
@admin.register(Transformation)
|
||||
class TransformationAdmin(admin.ModelAdmin):
|
||||
class TransformationAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
list_display = ['document_number', 'warehouse', 'status_display', 'date', 'employee', 'inputs_count', 'outputs_count']
|
||||
list_filter = ['status', 'warehouse', 'date']
|
||||
search_fields = ['document_number', 'comment']
|
||||
|
||||
Reference in New Issue
Block a user