Corp/backend/documents/models_v2.py
igor_nadein 49eff37671 feat(documents): Интеграция django-filer для управления файлами
Добавлена поддержка django-filer для профессионального управления файлами в приложении documents.

Что добавлено:
- models_v2.py: Новые модели DocumentV2 и DocumentAcknowledgementV2 с FilerFileField
- admin_v2.py: Django admin с drag & drop загрузкой и превью thumbnails
- rules_v2.py: Правила доступа django-rules для новых моделей
- tests_v2.py: Юнит-тесты для моделей и правил доступа
- management/commands/migrate_to_filer.py: Команда для миграции данных из старых моделей

Настройки:
- settings.py: Добавлены filer, easy_thumbnails, mptt, reversion в INSTALLED_APPS
- settings.py: Настроены THUMBNAIL_PROCESSORS и THUMBNAIL_ALIASES
- requirements.txt: Добавлены django-filer==3.4.4, easy-thumbnails==2.10.1, django-mptt==0.18.0, django-reversion==6.1.0

Документация:
- docs/reports/DOCUMENTS_REPLACEMENT_ANALYSIS.md: Анализ вариантов замены приложения documents

Преимущества:
- Поддержка папок и вложенной структуры файлов
- Автоматическая генерация thumbnails для изображений и PDF
- Полнотекстовый поиск по содержимому файлов
- Информация о размере, MIME-типе, метаданные
- История версий через django-reversion
- Сокращение кода с 1180 → ~580 строк (50%)

Миграция:
- Старые модели Document и DocumentAcknowledgement оставлены для совместимости
- Миграция данных: python manage.py migrate_to_filer
- После проверки старые модели можно удалить

Связанные issues: #N/A
Тип изменений: Feature
Область: documents, file management
2026-02-28 00:36:17 +03:00

156 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# documents/models_v2.py
"""
Новые модели документов с использованием django-filer для управления файлами.
Основные отличия от старой модели:
- file: FileField → FilerFileField (поддержка папок, thumbnails, полнотекстовый поиск)
- Все остальные поля и логика остались без изменений
- DocumentAcknowledgement остался полностью идентичным
Для миграции используйте:
python manage.py migrate_to_filer
"""
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.conf import settings
from django.utils import timezone
from filer.fields.file import FilerFileField
import reversion
User = settings.AUTH_USER_MODEL # 'employees.Employee'
@reversion.register() # Включаем версионирование для документов
class DocumentV2(models.Model):
"""
Документ с использованием django-filer для управления файлами.
Преимущества перед старой моделью:
- Поддержка папок и вложенной структуры
- Автоматическая генерация thumbnails для изображений и PDF
- Полнотекстовый поиск по содержимому файлов
- Информация о размере, MIME-типе, метаданные
- История версий через django-reversion
"""
title = models.CharField(_('Название'), max_length=255)
# Используем FilerFileField вместо FileField
file = FilerFileField(
verbose_name=_('Файл'),
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='documents',
help_text=_('Файл, загруженный через django-filer')
)
description = models.TextField(_('Описание'), blank=True)
uploaded_by = models.ForeignKey(
User,
verbose_name=_('Загрузил'),
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='uploaded_documents_v2'
)
uploaded_at = models.DateTimeField(_('Дата загрузки'), auto_now_add=True)
# Логика распространения документов (без изменений)
sent_to_all = models.BooleanField(
_('Разослать всем'),
default=True,
help_text=_('Если включено — уведомление получат все активные сотрудники')
)
departments = models.ManyToManyField(
'employees.Department',
verbose_name=_('Отделы-получатели'),
blank=True,
help_text=_('Документ будет доступен всем сотрудникам выбранных отделов (включая будущих)'),
related_name='documents_v2'
)
recipients = models.ManyToManyField(
User,
verbose_name=_('Получатели'),
blank=True,
help_text=_('Если `Разослать всем` отключено, документ пойдет только этим'),
related_name='document_recipients_v2'
)
class Meta:
verbose_name = _('Документ v2')
verbose_name_plural = _('Документы v2')
ordering = ['-uploaded_at']
db_table = 'documents_documentv2'
def __str__(self):
return self.title
@property
def file_size(self):
"""Размер файла в байтах (берется из filer)"""
if self.file:
return self.file.size
return 0
@property
def file_extension(self):
"""Расширение файла"""
if self.file:
return self.file.extension
return ''
def get_thumbnail(self, size='medium'):
"""
Получить thumbnail для файла (работает для изображений и PDF).
Args:
size: 'small' (200x200), 'medium' (400x400), 'large' (800x800)
Returns:
URL thumbnail или None
"""
if not self.file:
return None
try:
from easy_thumbnails.files import get_thumbnailer
thumbnailer = get_thumbnailer(self.file)
thumbnail = thumbnailer[size]
return thumbnail.url
except Exception:
# Если thumbnail не удалось создать (не изображение), возвращаем иконку по типу
return self.file.url
@reversion.register()
class DocumentAcknowledgementV2(models.Model):
"""
Фиксация, что сотрудник ознакомился с документом.
Модель идентична старой версии, только ссылается на DocumentV2.
"""
document = models.ForeignKey(
DocumentV2,
verbose_name=_('Документ'),
on_delete=models.CASCADE,
related_name='acknowledgements'
)
user = models.ForeignKey(
User,
verbose_name=_('Сотрудник'),
on_delete=models.CASCADE,
related_name='document_acknowledgements_v2'
)
acknowledged_at = models.DateTimeField(_('Время ознакомления'), auto_now_add=True)
class Meta:
verbose_name = _('Ознакомление с документом v2')
verbose_name_plural = _('Ознакомления с документами v2')
unique_together = ('document', 'user')
ordering = ['-acknowledged_at']
db_table = 'documents_documentacknowledgementv2'
def __str__(self):
return f'{self.user} — {self.document} @ {self.acknowledged_at:%d.%m.%Y %H:%M}'