When a StoragePath is changed, check if related documents require a rename

This commit is contained in:
Trenton H 2023-02-17 10:02:19 -08:00
parent 9893ae9880
commit 857944aabe
3 changed files with 53 additions and 37 deletions

View File

@ -414,13 +414,6 @@ structure as in the previous example above.
Defining a storage path is optional. If no storage path is defined for a
document, the global `PAPERLESS_FILENAME_FORMAT` is applied.
!!! warning
If you adjust the format of an existing storage path, old documents
don't get relocated automatically. You need to run the
[document renamer](/administration#renamer) to
adjust their paths.
## Celery Monitoring {#celery-monitoring}
The monitoring tool

View File

@ -27,6 +27,7 @@ from ..file_handling import generate_unique_filename
from ..models import Document
from ..models import MatchingModel
from ..models import PaperlessTask
from ..models import StoragePath
from ..models import Tag
logger = logging.getLogger("paperless.handlers")
@ -495,6 +496,16 @@ def update_filename_and_move_files(sender, instance, **kwargs):
)
@receiver(models.signals.post_save, sender=StoragePath)
def update_document_storage_path(sender, instance, **kwargs):
"""
Triggers when a storage path is changed, running against any documents using
the path, and checks to see if they need to be renamed
"""
for document in instance.documents.all():
update_filename_and_move_files(None, document)
def set_log_entry(sender, document=None, logging_group=None, **kwargs):
ct = ContentType.objects.get(model="document")

View File

@ -1,9 +1,6 @@
import datetime
import hashlib
import os
import random
import tempfile
import uuid
from pathlib import Path
from unittest import mock
@ -16,7 +13,6 @@ from django.utils import timezone
from ..file_handling import create_source_path_directory
from ..file_handling import delete_empty_directories
from ..file_handling import generate_filename
from ..file_handling import generate_unique_filename
from ..models import Correspondent
from ..models import Document
from ..models import DocumentType
@ -871,7 +867,7 @@ class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
self.assertTrue(os.path.isfile(doc.archive_path))
class TestFilenameGeneration(TestCase):
class TestFilenameGeneration(DirectoriesMixin, TestCase):
@override_settings(FILENAME_FORMAT="{title}")
def test_invalid_characters(self):
@ -1036,6 +1032,47 @@ class TestFilenameGeneration(TestCase):
self.assertEqual(generate_filename(doc_a), "0000002.pdf")
self.assertEqual(generate_filename(doc_b), "SomeImportantNone/2020-07-25.pdf")
def test_document_storage_path_changed(self):
"""
GIVEN:
- Document with a defined storage path
WHEN:
- The storage path format is updated
THEN:
- Document is renamed to the new format
"""
sp = StoragePath.objects.create(path="TestFolder/{created}")
document = Document.objects.create(
mime_type="application/pdf",
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
storage_path=sp,
)
# Ensure that filename is properly generated
document.filename = generate_filename(document)
self.assertEqual(
document.source_path,
os.path.join(settings.ORIGINALS_DIR, "TestFolder/2020-06-25.pdf"),
)
# Actually create the file
create_source_path_directory(document.source_path)
Path(document.source_path).touch()
self.assertTrue(os.path.isfile(document.source_path))
document.save()
# Change format
sp.path = "NewFolder/{created}"
sp.save()
document.refresh_from_db()
self.assertEqual(
document.source_path,
os.path.join(settings.ORIGINALS_DIR, "NewFolder/2020-06-25.pdf"),
)
self.assertTrue(os.path.isfile(document.source_path))
@override_settings(
FILENAME_FORMAT="{created_year_short}/{created_month_name_short}/{created_month_name}/{title}",
)
@ -1063,28 +1100,3 @@ class TestFilenameGeneration(TestCase):
checksum="2",
)
self.assertEqual(generate_filename(doc), "84/August/Aug/The Title.pdf")
def run():
doc = Document.objects.create(
checksum=str(uuid.uuid4()),
title=str(uuid.uuid4()),
content="wow",
)
doc.filename = generate_unique_filename(doc)
Path(doc.thumbnail_path).touch()
with open(doc.source_path, "w") as f:
f.write(str(uuid.uuid4()))
with open(doc.source_path, "rb") as f:
doc.checksum = hashlib.md5(f.read()).hexdigest()
with open(doc.archive_path, "w") as f:
f.write(str(uuid.uuid4()))
with open(doc.archive_path, "rb") as f:
doc.archive_checksum = hashlib.md5(f.read()).hexdigest()
doc.save()
for i in range(30):
doc.title = str(random.randrange(1, 5))
doc.save()