Исправлена ошибка 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,10 +1,16 @@
|
||||
from django.contrib import admin
|
||||
from user_roles.models import Role, UserRole
|
||||
from user_roles.mixins import OwnerOnlyAdminMixin
|
||||
from tenants.admin_mixins import TenantAdminOnlyMixin
|
||||
|
||||
|
||||
@admin.register(Role)
|
||||
class RoleAdmin(admin.ModelAdmin):
|
||||
class RoleAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
|
||||
"""
|
||||
Админка ролей.
|
||||
TenantAdminOnlyMixin - скрывает от public admin (localhost/admin/),
|
||||
так как таблица Role существует только в схемах тенантов.
|
||||
"""
|
||||
list_display = ['code', 'name', 'is_system']
|
||||
list_filter = ['is_system']
|
||||
search_fields = ['code', 'name']
|
||||
@@ -18,10 +24,12 @@ class RoleAdmin(admin.ModelAdmin):
|
||||
|
||||
|
||||
@admin.register(UserRole)
|
||||
class UserRoleAdmin(OwnerOnlyAdminMixin, admin.ModelAdmin):
|
||||
class UserRoleAdmin(TenantAdminOnlyMixin, OwnerOnlyAdminMixin, admin.ModelAdmin):
|
||||
"""
|
||||
Админка ролей пользователей.
|
||||
Доступна только владельцу.
|
||||
|
||||
TenantAdminOnlyMixin - скрывает от public admin (таблица только в tenant схемах)
|
||||
OwnerOnlyAdminMixin - доступна только владельцу магазина
|
||||
|
||||
ВАЖНО: UserRole изолирован по тенантам автоматически через django-tenants,
|
||||
поэтому владелец видит только пользователей своего магазина!
|
||||
|
||||
@@ -4,12 +4,25 @@
|
||||
ВАЖНО: Этот backend НЕ использует таблицы Django permissions из public schema!
|
||||
Он только эмулирует API has_perm(), читая роли из текущей tenant schema.
|
||||
Это безопасно для мультитенантной архитектуры.
|
||||
|
||||
ВАЖНО: Backend проверяет текущую схему перед обращением к tenant-only таблицам.
|
||||
В public схеме ролевые проверки пропускаются (fallback на стандартные Django permissions).
|
||||
"""
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
from django.db import connection
|
||||
from django_tenants.utils import get_public_schema_name
|
||||
from user_roles.services import RoleService
|
||||
from user_roles.models import Role
|
||||
|
||||
|
||||
def _is_public_schema():
|
||||
"""
|
||||
Проверяет, находимся ли мы в public схеме.
|
||||
В public схеме нет таблиц tenant-only моделей (UserRole, Role).
|
||||
"""
|
||||
return connection.schema_name == get_public_schema_name()
|
||||
|
||||
|
||||
class RoleBasedPermissionBackend(ModelBackend):
|
||||
"""
|
||||
Backend, который предоставляет права на основе роли пользователя в текущем тенанте.
|
||||
@@ -86,6 +99,11 @@ class RoleBasedPermissionBackend(ModelBackend):
|
||||
if user_obj.is_superuser:
|
||||
return True
|
||||
|
||||
# ВАЖНО: В public схеме нет таблиц UserRole/Role!
|
||||
# Используем только стандартные Django permissions.
|
||||
if _is_public_schema():
|
||||
return False
|
||||
|
||||
# Получаем роль пользователя в текущем тенанте
|
||||
# ВАЖНО: RoleService работает с текущей tenant schema!
|
||||
user_role = RoleService.get_user_role(user_obj)
|
||||
@@ -132,6 +150,11 @@ class RoleBasedPermissionBackend(ModelBackend):
|
||||
if user_obj.is_superuser:
|
||||
return True
|
||||
|
||||
# ВАЖНО: В public схеме нет таблиц UserRole/Role!
|
||||
# Используем только стандартные Django permissions.
|
||||
if _is_public_schema():
|
||||
return False
|
||||
|
||||
# Получаем роль пользователя в текущем тенанте
|
||||
user_role = RoleService.get_user_role(user_obj)
|
||||
if not user_role:
|
||||
|
||||
Reference in New Issue
Block a user