fix: add legacy file fallback and use atomic transactions for document creation

This commit is contained in:
kdeterme 2026-09-22 19:44:35 +02:00
parent 55cad6d6cc
commit c7c76f28dc
2 changed files with 109 additions and 90 deletions

View file

@ -731,9 +731,21 @@ class DocumentFolderAttachment(models.Model):
def duplicate_file(field): def duplicate_file(field):
if not field: if not field:
return None return None
file_name = os.path.basename(field.name) try:
with field.open("rb") as f: file_name = os.path.basename(field.name)
return ContentFile(f.read(), name=file_name) with field.open("rb") as f:
return ContentFile(f.read(), name=file_name)
except Exception:
# Fallback si le fichier historique est présent dans MEDIA_ROOT avant migration vers private storage
try:
if hasattr(settings, "MEDIA_ROOT") and field.name:
alt_path = os.path.join(settings.MEDIA_ROOT, field.name)
if os.path.exists(alt_path):
with open(alt_path, "rb") as f:
return ContentFile(f.read(), name=os.path.basename(field.name))
except Exception:
pass
return None
def ensure_document_for_legacy(instance, old_file_name=None): def ensure_document_for_legacy(instance, old_file_name=None):
@ -757,51 +769,57 @@ def ensure_document_for_legacy(instance, old_file_name=None):
context_object = instance.get_document_context() context_object = instance.get_document_context()
title = instance.get_document_title() title = instance.get_document_title()
if managed is None:
managed = ManagedDocument.objects.create(
title=title,
description=getattr(instance, "description", "") or "",
created_by=getattr(instance, "uploaded_by", None),
)
instance.__class__.objects.filter(pk=instance.pk).update(managed_document=managed)
instance.managed_document = managed
if context_object is not None:
DocumentAttachment.objects.get_or_create(
document=managed,
content_type=ContentType.objects.get_for_model(context_object),
object_id=context_object.pk,
defaults={"attached_by": getattr(instance, "uploaded_by", None)},
)
if hasattr(instance, "document_type") and instance.document_type:
label = instance.document_type
if hasattr(instance, "get_document_type_display"):
label = instance.get_document_type_display()
doc_type, _created = DocumentType.objects.get_or_create(
code=slugify(instance.document_type),
defaults={"label": label},
)
managed.types.add(doc_type)
file_changed = bool(old_file_name and old_file_name != instance.file.name) file_changed = bool(old_file_name and old_file_name != instance.file.name)
should_create_version = not managed.versions.exists() or file_changed should_create_version = (managed is None) or not managed.versions.exists() or file_changed
if should_create_version: if should_create_version:
new_file = duplicate_file(instance.file) new_file = duplicate_file(instance.file)
thumbnail_file = duplicate_file(getattr(instance, "thumbnail", None)) if not new_file:
# Ne pas créer de ManagedDocument orphelin si le fichier physique est inaccessible
return
version = DocumentVersion( with transaction.atomic():
document=managed, if managed is None:
file=new_file, managed = ManagedDocument.objects.create(
thumbnail=thumbnail_file, title=title,
uploaded_by=getattr(instance, "uploaded_by", None), description=getattr(instance, "description", "") or "",
description=getattr(instance, "description", "") or "", created_by=getattr(instance, "uploaded_by", None),
comment=_("Version synchronised from legacy module"), )
status=DocumentVersion.STATUS_APPROVED, instance.__class__.objects.filter(pk=instance.pk).update(managed_document=managed)
validated_at=timezone.now(), instance.managed_document = managed
)
version.save() if context_object is not None:
DocumentAttachment.objects.get_or_create(
document=managed,
content_type=ContentType.objects.get_for_model(context_object),
object_id=context_object.pk,
defaults={"attached_by": getattr(instance, "uploaded_by", None)},
)
if hasattr(instance, "document_type") and instance.document_type:
label = instance.document_type
if hasattr(instance, "get_document_type_display"):
label = instance.get_document_type_display()
doc_type, _created = DocumentType.objects.get_or_create(
code=slugify(instance.document_type),
defaults={"label": label},
)
managed.types.add(doc_type)
thumbnail_file = duplicate_file(getattr(instance, "thumbnail", None))
version = DocumentVersion(
document=managed,
file=new_file,
thumbnail=thumbnail_file,
uploaded_by=getattr(instance, "uploaded_by", None),
description=getattr(instance, "description", "") or "",
comment=_("Version synchronised from legacy module"),
status=DocumentVersion.STATUS_APPROVED,
validated_at=timezone.now(),
)
version.save()
managed.touch(version)
elif managed.current_version is None: elif managed.current_version is None:
managed.touch(managed.versions.order_by("-version_number").first()) managed.touch(managed.versions.order_by("-version_number").first())

View file

@ -331,57 +331,58 @@ class DocumentManagementService:
"message": _("New version added to existing document."), "message": _("New version added to existing document."),
} }
else: else:
# Déterminer la visibilité with transaction.atomic():
if visibility is None: # Déterminer la visibilité
if content_object and getattr(getattr(content_object, '_meta', None), 'model_name', None) == 'project': if visibility is None:
doc_visibility = VisibilityScope.SCOPED if content_object and getattr(getattr(content_object, '_meta', None), 'model_name', None) == 'project':
elif target_folder and getattr(target_folder, 'visibility', None) == VisibilityScope.SCOPED: doc_visibility = VisibilityScope.SCOPED
doc_visibility = VisibilityScope.SCOPED elif target_folder and getattr(target_folder, 'visibility', None) == VisibilityScope.SCOPED:
doc_visibility = VisibilityScope.SCOPED
else:
doc_visibility = VisibilityScope.INTERNAL
else: else:
doc_visibility = VisibilityScope.INTERNAL doc_visibility = visibility
else:
doc_visibility = visibility
# Créer un nouveau document # Créer un nouveau document
doc_title = title or (file.name if hasattr(file, "name") else _("Document")) doc_title = title or (file.name if hasattr(file, "name") else _("Document"))
document = ManagedDocument.objects.create( document = ManagedDocument.objects.create(
title=doc_title, title=doc_title,
description=description or "", description=description or "",
created_by=user, created_by=user,
unique_params=normalized_params, unique_params=normalized_params,
visibility=doc_visibility, visibility=doc_visibility,
)
# Ajouter le tag
if tag:
document.tags.add(tag)
# Ajouter au répertoire
if target_folder:
document.folders.add(target_folder)
# Créer la première version
version = DocumentVersion(
document=document,
file=file,
uploaded_by=user,
description=description or "",
comment=_("Initial version"),
status=DocumentVersion.STATUS_PENDING,
)
version.save()
# Attacher à l'objet de contexte si fourni
if content_object:
content_type = ContentType.objects.get_for_model(content_object)
DocumentAttachment.objects.get_or_create(
document=document,
content_type=content_type,
object_id=content_object.pk,
defaults={"attached_by": user},
) )
# Ajouter le tag
if tag:
document.tags.add(tag)
# Ajouter au répertoire
if target_folder:
document.folders.add(target_folder)
# Créer la première version
version = DocumentVersion(
document=document,
file=file,
uploaded_by=user,
description=description or "",
comment=_("Initial version"),
status=DocumentVersion.STATUS_PENDING,
)
version.save()
# Attacher à l'objet de contexte si fourni
if content_object:
content_type = ContentType.objects.get_for_model(content_object)
DocumentAttachment.objects.get_or_create(
document=document,
content_type=content_type,
object_id=content_object.pk,
defaults={"attached_by": user},
)
return { return {
"document": document, "document": document,
"version": version, "version": version,