Fix: select_related in remove_doclink() to avoid signal-triggered reload

Same pattern as the update_or_create() fix: target_doc_field_instance was
fetched without select_related, so its .document/.field weren't cached
when .save() fired the post_save signal -- auditlog's receiver touching
.document re-fetched it, once per (source, target) pair being unlinked
with no batching across calls. Also benefits the single-document PATCH
path in serialisers.py, which calls the same helper.

Broadened the removal test's query assertion now that both sides are fixed.
This commit is contained in:
stumpylog
2026-08-27 09:42:14 -07:00
parent 02c547e856
commit 013fe0baff
2 changed files with 13 additions and 12 deletions
+7 -4
View File
@@ -1205,10 +1205,13 @@ def remove_doclink(
"""
Removes a 'symmetrical' link to `document` from the target document's existing custom field instance
"""
target_doc_field_instance = CustomFieldInstance.objects.filter(
document_id=target_doc_id,
field=field,
).first()
# select_related: a signal receiver (auditlog) touches .document/.field
# on save() below -- without this, that's a per-call reload query.
target_doc_field_instance = (
CustomFieldInstance.objects.filter(document_id=target_doc_id, field=field)
.select_related("document", "field")
.first()
)
if (
target_doc_field_instance is not None
and document.id in target_doc_field_instance.value
+6 -8
View File
@@ -500,9 +500,7 @@ class TestBulkEdit(DirectoriesMixin, TestCase):
- The field is removed from all of them in one call
THEN:
- The symmetrical links are removed from the target
- The source documents come from one batched query, not one
lookup per source document (the target side, fetched inside
remove_doclink(), is untouched by this PR and out of scope)
- No per-document lookup query is issued, on either side
"""
target = Document.objects.create(checksum="rm-target", title="rm-target")
docs = [
@@ -530,17 +528,17 @@ class TestBulkEdit(DirectoriesMixin, TestCase):
remove_custom_fields=[field.id],
)
source_doc_lookups = [
single_document_lookups = [
q
for q in ctx.captured_queries
if 'FROM "documents_document"' in q["sql"]
and any(f'."id" = {doc.id} ' in q["sql"] for doc in docs)
and '"documents_document"."id" = ' in q["sql"]
]
self.assertEqual(
source_doc_lookups,
single_document_lookups,
[],
"Expected source documents to come from a batched query, not "
f"per-document lookups, got: {source_doc_lookups}",
"Expected batched document resolution, not per-document lookups, "
f"got: {single_document_lookups}",
)
self.assertEqual(target.custom_fields.get(field=field).value, [])