Fixhancement: include trashed documents in document exporter/importer

This commit is contained in:
Jan Kleine
2026-03-28 10:32:45 +01:00
parent 9383471fa0
commit 2bf028d37b
3 changed files with 61 additions and 5 deletions
@@ -388,7 +388,7 @@ class Command(CryptMixin, PaperlessCommand):
"custom_field_instances": CustomFieldInstance.objects.all(),
"app_configs": ApplicationConfiguration.objects.all(),
"notes": Note.objects.all(),
"documents": Document.objects.order_by("id").all(),
"documents": Document.global_objects.order_by("id").all(),
"social_accounts": SocialAccount.objects.all(),
"social_apps": SocialApp.objects.all(),
"social_tokens": SocialToken.objects.all(),
@@ -443,7 +443,7 @@ class Command(CryptMixin, PaperlessCommand):
writer.write_batch(batch)
document_map: dict[int, Document] = {
d.pk: d for d in Document.objects.order_by("id")
d.pk: d for d in Document.global_objects.order_by("id")
}
# 3. Export files from each document
@@ -125,7 +125,7 @@ class Command(CryptMixin, PaperlessCommand):
"Found existing user(s), this might indicate a non-empty installation",
),
)
if Document.objects.count() != 0:
if Document.global_objects.count() != 0:
self.stdout.write(
self.style.WARNING(
"Found existing documents(s), this might indicate a non-empty installation",
@@ -376,7 +376,7 @@ class Command(CryptMixin, PaperlessCommand):
]
for record in self.track(document_records, description="Copying files..."):
document = Document.objects.get(pk=record["pk"])
document = Document.global_objects.get(pk=record["pk"])
doc_file = record[EXPORTER_FILE_NAME]
document_path = self.source / doc_file
@@ -389,7 +389,7 @@ class TestExportImport(
self.assertIsFile(
str(self.target / doc_from_manifest[EXPORTER_FILE_NAME]),
)
self.d3.delete()
self.d3.hard_delete()
manifest = self._do_export()
self.assertRaises(
@@ -868,6 +868,62 @@ class TestExportImport(
for obj in manifest:
self.assertNotEqual(obj["model"], "auditlog.logentry")
def test_export_import_soft_deleted_document(self) -> None:
"""
GIVEN:
- A document has been soft-deleted
WHEN:
- Export and re-import are performed
THEN:
- The soft-deleted document is included in the manifest
- The soft-deleted document is re-imported successfully
- The deleted_at timestamp is preserved after import
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
# Soft-delete one document
self.d2.delete()
self.d2.refresh_from_db()
self.assertIsNotNone(self.d2.deleted_at)
# Non-deleted documents visible via .objects should be 3
self.assertEqual(Document.objects.count(), 3)
# All documents including soft-deleted should be 4
self.assertEqual(Document.global_objects.count(), 4)
manifest = self._do_export()
# Manifest must contain all 4 documents, including the soft-deleted one
doc_records = [e for e in manifest if e["model"] == "documents.document"]
self.assertEqual(len(doc_records), 4)
exported_pks = {r["pk"] for r in doc_records}
self.assertIn(self.d2.pk, exported_pks)
# Re-import
with paperless_environment():
Document.global_objects.all().hard_delete()
Correspondent.objects.all().delete()
DocumentType.objects.all().delete()
Tag.objects.all().delete()
self.assertEqual(Document.global_objects.count(), 0)
call_command(
"document_importer",
"--no-progress-bar",
self.target,
skip_checks=True,
)
self.assertEqual(Document.global_objects.count(), 4)
# The soft-deleted document should still have deleted_at set
reimported = Document.global_objects.get(pk=self.d2.pk)
self.assertIsNotNone(reimported.deleted_at)
def test_export_data_only(self) -> None:
"""
GIVEN: