fix: Исправлена ошибка ForeignKey в products - замена PlatformAdmin на CustomUser
Устранена ошибка ValueError "Cannot query 'email': Must be 'PlatformAdmin' instance"
при доступе CustomUser к странице /products/.
Проблема:
- Модели products (TENANT_APPS) использовали get_user_model() для ForeignKey
- get_user_model() возвращал PlatformAdmin (AUTH_USER_MODEL)
- Но tenant модели должны ссылаться на CustomUser (tenant пользователей)
- Это создавало конфликт типов при запросах от CustomUser
Изменения:
1. products/models/base.py:
- Убран get_user_model()
- BaseProductEntity.archived_by теперь ForeignKey('accounts.CustomUser')
2. products/models/categories.py:
- Убран get_user_model()
- ProductCategory.deleted_by теперь ForeignKey('accounts.CustomUser')
3. products/models/import_job.py:
- Убран get_user_model()
- ProductImportJob.user теперь ForeignKey('accounts.CustomUser')
4. Создана миграция 0002 с data migration:
- Очистка некорректных ссылок (установка NULL)
- Изменение типа ForeignKey полей с PlatformAdmin на CustomUser
5. user_roles/auth_backend.py:
- Добавлена функция _is_tenant_user() для проверки типа пользователя
- Исправлена логика has_perm() и has_module_perms()
- CustomUser теперь не проверяется через ModelBackend.has_perm()
6. admin_access_middleware.py:
- Улучшены сообщения об ошибках доступа
- Добавлен рендеринг через шаблон access_denied.html
7. templates/errors/access_denied.html:
- Новый шаблон для красивого отображения ошибок доступа
Результат:
- CustomUser может без ошибок работать со страницей /products/
- Корректная архитектура: tenant модели ссылаются на tenant пользователей
- PlatformAdmin продолжает работать корректно
- Чистое решение без костылей
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,15 @@ def _is_public_schema():
|
||||
return connection.schema_name == get_public_schema_name()
|
||||
|
||||
|
||||
def _is_tenant_user(user_obj):
|
||||
"""
|
||||
Проверяет, является ли пользователь CustomUser (пользователь тенанта).
|
||||
PlatformAdmin не имеет ролей в тенантах.
|
||||
"""
|
||||
from accounts.models import CustomUser
|
||||
return isinstance(user_obj, CustomUser)
|
||||
|
||||
|
||||
class RoleBasedPermissionBackend(ModelBackend):
|
||||
"""
|
||||
Backend, который предоставляет права на основе роли пользователя в текущем тенанте.
|
||||
@@ -86,15 +95,20 @@ class RoleBasedPermissionBackend(ModelBackend):
|
||||
Returns:
|
||||
bool: True если пользователь имеет разрешение
|
||||
"""
|
||||
# Сначала проверяем стандартные permissions через ModelBackend
|
||||
# (для superuser, staff с Django permissions и т.д.)
|
||||
if super().has_perm(user_obj, perm, obj):
|
||||
return True
|
||||
|
||||
# Если пользователь не аутентифицирован, нет доступа
|
||||
if not user_obj.is_authenticated:
|
||||
return False
|
||||
|
||||
# Для CustomUser (пользователи тенантов) пропускаем стандартную проверку Django permissions
|
||||
# т.к. AUTH_USER_MODEL = PlatformAdmin, и super().has_perm() будет пытаться
|
||||
# искать permissions для PlatformAdmin, а не для CustomUser
|
||||
is_tenant = _is_tenant_user(user_obj)
|
||||
|
||||
if not is_tenant:
|
||||
# Для PlatformAdmin проверяем стандартные permissions через ModelBackend
|
||||
if super().has_perm(user_obj, perm, obj):
|
||||
return True
|
||||
|
||||
# Суперпользователь имеет все права
|
||||
if user_obj.is_superuser:
|
||||
return True
|
||||
@@ -104,7 +118,7 @@ class RoleBasedPermissionBackend(ModelBackend):
|
||||
if _is_public_schema():
|
||||
return False
|
||||
|
||||
# Получаем роль пользователя в текущем тенанте
|
||||
# Для CustomUser получаем роль пользователя в текущем тенанте
|
||||
# ВАЖНО: RoleService работает с текущей tenant schema!
|
||||
user_role = RoleService.get_user_role(user_obj)
|
||||
if not user_role:
|
||||
@@ -138,14 +152,18 @@ class RoleBasedPermissionBackend(ModelBackend):
|
||||
Returns:
|
||||
bool: True если пользователь имеет хотя бы одно разрешение для приложения
|
||||
"""
|
||||
# Сначала проверяем стандартные permissions через ModelBackend
|
||||
if super().has_module_perms(user_obj, app_label):
|
||||
return True
|
||||
|
||||
# Если пользователь не аутентифицирован, нет доступа
|
||||
if not user_obj.is_authenticated:
|
||||
return False
|
||||
|
||||
# Для CustomUser (пользователи тенантов) пропускаем стандартную проверку Django permissions
|
||||
is_tenant = _is_tenant_user(user_obj)
|
||||
|
||||
if not is_tenant:
|
||||
# Для PlatformAdmin проверяем стандартные permissions через ModelBackend
|
||||
if super().has_module_perms(user_obj, app_label):
|
||||
return True
|
||||
|
||||
# Суперпользователь имеет все права
|
||||
if user_obj.is_superuser:
|
||||
return True
|
||||
@@ -155,7 +173,7 @@ class RoleBasedPermissionBackend(ModelBackend):
|
||||
if _is_public_schema():
|
||||
return False
|
||||
|
||||
# Получаем роль пользователя в текущем тенанте
|
||||
# Для CustomUser получаем роль пользователя в текущем тенанте
|
||||
user_role = RoleService.get_user_role(user_obj)
|
||||
if not user_role:
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user