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:
@@ -0,0 +1,68 @@
|
||||
# Generated by Django 5.0.10 on 2026-01-09 20:42
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def clear_invalid_user_references(apps, schema_editor):
|
||||
"""
|
||||
Очистить ссылки на PlatformAdmin в полях archived_by/deleted_by/user,
|
||||
т.к. они теперь ссылаются на CustomUser.
|
||||
|
||||
Устанавливаем NULL для всех существующих ссылок на пользователей,
|
||||
чтобы избежать ошибок referential integrity при изменении типа ForeignKey.
|
||||
"""
|
||||
ProductCategory = apps.get_model('products', 'ProductCategory')
|
||||
Product = apps.get_model('products', 'Product')
|
||||
ProductKit = apps.get_model('products', 'ProductKit')
|
||||
ConfigurableProduct = apps.get_model('products', 'ConfigurableProduct')
|
||||
ProductImportJob = apps.get_model('products', 'ProductImportJob')
|
||||
|
||||
# Очищаем все существующие ссылки (ставим NULL)
|
||||
ProductCategory.objects.all().update(deleted_by=None)
|
||||
Product.objects.all().update(archived_by=None)
|
||||
ProductKit.objects.all().update(archived_by=None)
|
||||
ConfigurableProduct.objects.all().update(archived_by=None)
|
||||
|
||||
# ProductImportJob.user не может быть NULL (CASCADE), поэтому удаляем записи
|
||||
# если они были созданы PlatformAdmin (что маловероятно)
|
||||
ProductImportJob.objects.all().delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0001_initial'),
|
||||
('products', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
# Сначала очищаем некорректные ссылки
|
||||
migrations.RunPython(clear_invalid_user_references, reverse_code=migrations.RunPython.noop),
|
||||
# Затем изменяем типы полей
|
||||
migrations.AlterField(
|
||||
model_name='configurableproduct',
|
||||
name='archived_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='archived_%(class)s_set', to='accounts.customuser', verbose_name='Архивировано пользователем'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='product',
|
||||
name='archived_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='archived_%(class)s_set', to='accounts.customuser', verbose_name='Архивировано пользователем'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='productcategory',
|
||||
name='deleted_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='deleted_categories', to='accounts.customuser', verbose_name='Удалена пользователем'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='productimportjob',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='product_import_jobs', to='accounts.customuser', verbose_name='Пользователь'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='productkit',
|
||||
name='archived_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='archived_%(class)s_set', to='accounts.customuser', verbose_name='Архивировано пользователем'),
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user