Обновления в user_roles: модели, сервисы, представления и миграции
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
# Generated by Django 5.0.10 on 2026-01-03 23:23
|
||||
# Generated by Django 5.0.10 on 2026-01-08 15:58
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
@@ -10,7 +9,7 @@ class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('accounts', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
@@ -36,9 +35,9 @@ class Migration(migrations.Migration):
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
|
||||
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_user_roles', to=settings.AUTH_USER_MODEL, verbose_name='Создал')),
|
||||
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_user_roles', to='accounts.customuser', verbose_name='Создал')),
|
||||
('role', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='users', to='user_roles.role', verbose_name='Роль')),
|
||||
('user', models.OneToOneField(help_text='Пользователь из public schema (SHARED_APPS)', on_delete=django.db.models.deletion.CASCADE, related_name='tenant_role', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь')),
|
||||
('user', models.OneToOneField(help_text='Пользователь тенанта (из tenant schema)', on_delete=django.db.models.deletion.CASCADE, related_name='tenant_role', to='accounts.customuser', verbose_name='Пользователь')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Роль пользователя',
|
||||
|
||||
@@ -13,18 +13,23 @@ class Role(models.Model):
|
||||
|
||||
Не нужно явно связывать с тенантом через FK - изоляция происходит автоматически!
|
||||
"""
|
||||
PLATFORM_SUPPORT = 'platform_support'
|
||||
OWNER = 'owner'
|
||||
MANAGER = 'manager'
|
||||
FLORIST = 'florist'
|
||||
COURIER = 'courier'
|
||||
|
||||
ROLE_CHOICES = [
|
||||
(PLATFORM_SUPPORT, 'Техподдержка платформы'),
|
||||
(OWNER, 'Владелец'),
|
||||
(MANAGER, 'Менеджер'),
|
||||
(FLORIST, 'Флорист'),
|
||||
(COURIER, 'Курьер'),
|
||||
]
|
||||
|
||||
# Роли, скрытые от пользователей тенанта (не отображаются в списках)
|
||||
HIDDEN_ROLES = [PLATFORM_SUPPORT]
|
||||
|
||||
code = models.CharField(
|
||||
max_length=20,
|
||||
choices=ROLE_CHOICES,
|
||||
@@ -67,11 +72,11 @@ class UserRole(models.Model):
|
||||
Один пользователь = одна роль в рамках одного тенанта.
|
||||
"""
|
||||
user = models.OneToOneField(
|
||||
settings.AUTH_USER_MODEL,
|
||||
'accounts.CustomUser',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='tenant_role',
|
||||
verbose_name="Пользователь",
|
||||
help_text="Пользователь из public schema (SHARED_APPS)"
|
||||
help_text="Пользователь тенанта (из tenant schema)"
|
||||
)
|
||||
role = models.ForeignKey(
|
||||
Role,
|
||||
@@ -81,7 +86,7 @@ class UserRole(models.Model):
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
created_by = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
'accounts.CustomUser',
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
|
||||
@@ -18,6 +18,12 @@ class RoleService:
|
||||
поэтому роли создаются в schema конкретного тенанта автоматически!
|
||||
"""
|
||||
default_roles = [
|
||||
{
|
||||
'code': Role.PLATFORM_SUPPORT,
|
||||
'name': 'Техподдержка платформы',
|
||||
'description': 'Служебный аккаунт техподдержки платформы. Полный доступ для помощи владельцу.',
|
||||
'is_system': True,
|
||||
},
|
||||
{
|
||||
'code': Role.OWNER,
|
||||
'name': 'Владелец',
|
||||
@@ -50,6 +56,26 @@ class RoleService:
|
||||
defaults=role_data
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_visible_roles():
|
||||
"""
|
||||
Возвращает роли, видимые для пользователей тенанта.
|
||||
Исключает служебные роли (platform_support).
|
||||
"""
|
||||
return Role.objects.exclude(code__in=Role.HIDDEN_ROLES)
|
||||
|
||||
@staticmethod
|
||||
def get_visible_users():
|
||||
"""
|
||||
Возвращает пользователей, видимых для владельца тенанта.
|
||||
Исключает пользователей с ролью platform_support.
|
||||
"""
|
||||
from accounts.models import CustomUser
|
||||
hidden_user_ids = UserRole.objects.filter(
|
||||
role__code__in=Role.HIDDEN_ROLES
|
||||
).values_list('user_id', flat=True)
|
||||
return CustomUser.objects.exclude(id__in=hidden_user_ids)
|
||||
|
||||
@staticmethod
|
||||
def get_role_by_code(code):
|
||||
"""Получить роль по коду"""
|
||||
|
||||
@@ -15,17 +15,23 @@ def user_role_list(request):
|
||||
"""Список пользователей с их ролями"""
|
||||
# Фильтр по активности
|
||||
show_inactive = request.GET.get('show_inactive', 'false') == 'true'
|
||||
|
||||
|
||||
# Базовый queryset - исключаем скрытые роли (platform_support)
|
||||
base_qs = UserRole.objects.select_related('user', 'role', 'created_by').exclude(
|
||||
role__code__in=Role.HIDDEN_ROLES
|
||||
)
|
||||
|
||||
if show_inactive:
|
||||
user_roles = UserRole.objects.select_related('user', 'role', 'created_by').all()
|
||||
user_roles = base_qs.all()
|
||||
else:
|
||||
# По умолчанию показываем только активных
|
||||
user_roles = UserRole.objects.select_related('user', 'role', 'created_by').filter(
|
||||
user_roles = base_qs.filter(
|
||||
is_active=True,
|
||||
user__is_active=True
|
||||
)
|
||||
|
||||
roles = Role.objects.all()
|
||||
|
||||
# Показываем только видимые роли (без platform_support)
|
||||
roles = RoleService.get_visible_roles()
|
||||
|
||||
context = {
|
||||
'user_roles': user_roles,
|
||||
@@ -69,7 +75,7 @@ def user_role_create(request):
|
||||
f'Пользователь с email {email} уже существует в системе.'
|
||||
)
|
||||
# ВАЖНО: Возвращаемся к форме, чтобы пользователь мог исправить email
|
||||
roles = Role.objects.all()
|
||||
roles = RoleService.get_visible_roles()
|
||||
context = {'roles': roles}
|
||||
return render(request, 'user_roles/user_role_create.html', context)
|
||||
else:
|
||||
@@ -100,7 +106,8 @@ def user_role_create(request):
|
||||
except Exception as e:
|
||||
messages.error(request, f'Ошибка при создании пользователя: {str(e)}')
|
||||
|
||||
roles = Role.objects.all()
|
||||
# Показываем только видимые роли (без platform_support)
|
||||
roles = RoleService.get_visible_roles()
|
||||
context = {
|
||||
'roles': roles,
|
||||
}
|
||||
@@ -113,6 +120,11 @@ def user_role_edit(request, pk):
|
||||
"""Изменение роли пользователя"""
|
||||
user_role = get_object_or_404(UserRole, pk=pk)
|
||||
|
||||
# Защита от редактирования скрытых ролей (platform_support)
|
||||
if user_role.role.code in Role.HIDDEN_ROLES:
|
||||
messages.error(request, 'Недостаточно прав для редактирования этого пользователя')
|
||||
return redirect('user_roles:list')
|
||||
|
||||
# Защита от самоблокировки
|
||||
can_modify, error_message = RoleService.can_modify_user_role(request.user, user_role)
|
||||
if not can_modify:
|
||||
@@ -130,9 +142,9 @@ def user_role_edit(request, pk):
|
||||
user_role.user.save()
|
||||
|
||||
messages.success(request, f'Пароль для пользователя {user_role.user.email} успешно обновлен')
|
||||
|
||||
|
||||
# Показываем новый пароль в модальном окне
|
||||
roles = Role.objects.all()
|
||||
roles = RoleService.get_visible_roles()
|
||||
context = {
|
||||
'user_role': user_role,
|
||||
'roles': roles,
|
||||
@@ -170,7 +182,7 @@ def user_role_edit(request, pk):
|
||||
except Exception as e:
|
||||
messages.error(request, f'Ошибка при обновлении данных: {str(e)}')
|
||||
|
||||
roles = Role.objects.all()
|
||||
roles = RoleService.get_visible_roles()
|
||||
context = {
|
||||
'user_role': user_role,
|
||||
'roles': roles,
|
||||
@@ -184,6 +196,11 @@ def user_role_delete(request, pk):
|
||||
"""Деактивация пользователя (soft delete)"""
|
||||
user_role = get_object_or_404(UserRole, pk=pk)
|
||||
|
||||
# Защита от удаления скрытых ролей (platform_support)
|
||||
if user_role.role.code in Role.HIDDEN_ROLES:
|
||||
messages.error(request, 'Недостаточно прав для удаления этого пользователя')
|
||||
return redirect('user_roles:list')
|
||||
|
||||
# Защита от самоблокировки
|
||||
can_modify, error_message = RoleService.can_modify_user_role(request.user, user_role)
|
||||
if not can_modify:
|
||||
@@ -215,6 +232,11 @@ def user_role_reactivate(request, pk):
|
||||
"""Реактивация пользователя"""
|
||||
user_role = get_object_or_404(UserRole, pk=pk)
|
||||
|
||||
# Защита от реактивации скрытых ролей (platform_support)
|
||||
if user_role.role.code in Role.HIDDEN_ROLES:
|
||||
messages.error(request, 'Недостаточно прав для изменения этого пользователя')
|
||||
return redirect('user_roles:list')
|
||||
|
||||
if request.method == 'POST':
|
||||
email = user_role.user.email
|
||||
|
||||
|
||||
Reference in New Issue
Block a user