116 lines
3.8 KiB
Python
116 lines
3.8 KiB
Python
# api/v1/documents/fields.py
|
||
"""
|
||
Кастомные serializer fields для работы с django-filer.
|
||
"""
|
||
|
||
import logging
|
||
|
||
from django.core.files.uploadedfile import UploadedFile
|
||
from drf_spectacular.types import OpenApiTypes
|
||
from drf_spectacular.utils import extend_schema_field
|
||
from filer.models import File as FilerFile
|
||
from rest_framework import serializers
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
@extend_schema_field(OpenApiTypes.URI)
|
||
class FilerFileField(serializers.Field):
|
||
"""
|
||
Serializer field для работы с django-filer FilerFileField.
|
||
|
||
При чтении (GET):
|
||
- Возвращает URL файла из filer.File.url
|
||
|
||
При записи (POST/PUT/PATCH):
|
||
- Принимает UploadedFile
|
||
- Создает filer.File объект
|
||
- Возвращает filer.File instance для присвоения в модель
|
||
"""
|
||
|
||
def to_representation(self, value):
|
||
"""
|
||
Преобразует filer.File в URL для API response.
|
||
|
||
Args:
|
||
value: filer.File instance или None
|
||
|
||
Returns:
|
||
str: Полный URL файла или None
|
||
"""
|
||
if value is None:
|
||
return None
|
||
|
||
if isinstance(value, FilerFile):
|
||
if not value.url:
|
||
return None
|
||
|
||
# Получаем request из контекста для построения абсолютного URL
|
||
request = self.context.get("request")
|
||
if request:
|
||
return request.build_absolute_uri(value.url)
|
||
|
||
# Fallback: возвращаем относительный URL
|
||
return value.url
|
||
|
||
return None
|
||
|
||
def to_internal_value(self, data):
|
||
"""
|
||
Преобразует UploadedFile в filer.File instance.
|
||
|
||
Args:
|
||
data: UploadedFile из request
|
||
|
||
Returns:
|
||
FilerFile: Созданный filer.File объект
|
||
|
||
Raises:
|
||
serializers.ValidationError: При ошибках валидации
|
||
"""
|
||
if data is None:
|
||
return None
|
||
|
||
if not isinstance(data, UploadedFile):
|
||
raise serializers.ValidationError("Ожидается файл для загрузки.")
|
||
|
||
# Получаем пользователя из контекста
|
||
request = self.context.get("request")
|
||
owner = getattr(request, "user", None) if request else None
|
||
|
||
try:
|
||
# Создаем filer.File объект
|
||
# is_public=True делает файл доступным без авторизации
|
||
filer_file = FilerFile.objects.create(
|
||
file=data,
|
||
original_filename=data.name,
|
||
name=data.name,
|
||
owner=owner,
|
||
is_public=True, # Делаем файл публичным для доступа с frontend
|
||
)
|
||
|
||
logger.info(
|
||
"Document file created",
|
||
extra={
|
||
"event": "document.file.created",
|
||
"file_id": filer_file.id,
|
||
"owner_id": owner.id if owner else None,
|
||
"is_public": True,
|
||
"size_bytes": data.size,
|
||
},
|
||
)
|
||
|
||
return filer_file
|
||
|
||
except Exception as e:
|
||
logger.exception(
|
||
"Document file creation failed",
|
||
extra={
|
||
"event": "document.file.creation_failed",
|
||
"owner_id": owner.id if owner else None,
|
||
"size_bytes": data.size,
|
||
"error_type": type(e).__name__,
|
||
},
|
||
)
|
||
raise serializers.ValidationError(f"Ошибка при создании файла: {str(e)}")
|