Repository: learningequality/studio
Issue: ContentNode.DoesNotExist: ContentNode matching query does not exist.

No need to throw when the delete node task does not find the node it is deleting - its work is done!
Sentry Issue: [STUDIO-EF4](https://sentry.io/organizations/learningequality/issues/2448220046/?referrer=github_integration)

```
ContentNode.DoesNotExist: ContentNode matching query does not exist.
(5 additional frame(s) were not displayed)
...
  File "model_utils/tracker.py", line 65, in __set__
    getattr(instance, self.field_name)
  File "model_utils/tracker.py", line 42, in __get__
    value = self.descriptor.__get__(instance, owner)
  File "django/db/models/query_utils.py", line 144, in __get__
    instance.refresh_from_db(fields=[field_name])
  File "django/db/models/base.py", line 637, in refresh_from_db
    db_instance = db_instance_qs.get()
  File "django/db/models/query.py", line 437, in get
    self.model._meta.object_name
```

Changed files:
contentcuration/contentcuration/tests/test_asynctask.py, contentcuration/contentcuration/tasks.py

diff --git a/contentcuration/contentcuration/tasks.py b/contentcuration/contentcuration/tasks.py
index 0fc3905b2..602aebadf 100644
--- a/contentcuration/contentcuration/tasks.py
+++ b/contentcuration/contentcuration/tasks.py
@@ -12,8 +12,8 @@ from django.core.mail import EmailMessage
 from django.db import IntegrityError
 from django.db.utils import OperationalError
 from django.template.loader import render_to_string
-from django.utils.translation import override
 from django.utils.translation import gettext as _
+from django.utils.translation import override
 
 from contentcuration.celery import app
 from contentcuration.models import Channel
@@ -51,10 +51,13 @@ def delete_node_task(
     channel_id,
     node_id,
 ):
-    node = ContentNode.objects.get(id=node_id)
-
     deleted = False
     attempts = 0
+    try:
+        node = ContentNode.objects.get(id=node_id)
+    except ContentNode.DoesNotExist:
+        deleted = True
+
     try:
         while not deleted and attempts < 10:
             try:
diff --git a/contentcuration/contentcuration/tests/test_asynctask.py b/contentcuration/contentcuration/tests/test_asynctask.py
index 15097fe3a..46ce9b5b8 100644
--- a/contentcuration/contentcuration/tests/test_asynctask.py
+++ b/contentcuration/contentcuration/tests/test_asynctask.py
@@ -154,6 +154,39 @@ class AsyncTaskTestCase(BaseAPITestCase):
                 assert child.original_source_node_id in node_ids
                 assert child.source_node_id in node_ids
 
+    def test_delete_nodes_task(self):
+        node_id = "00000000000000000000000000000002"
+
+        task_args = {
+            "user_id": self.user.pk,
+            "channel_id": self.channel.pk,
+            "node_id": node_id,
+        }
+        create_async_task(
+            "delete-node", self.user, apply_async=False, **task_args
+        )
+
+        try:
+            ContentNode.objects.get(
+                id=node_id
+            )
+            self.fail("Node still exists after deletion task")
+        except ContentNode.DoesNotExist:
+            pass
+
+    def test_delete_nodes_task_no_node(self):
+        node_id = uuid.uuid4().hex
+
+        task_args = {
+            "user_id": self.user.pk,
+            "channel_id": self.channel.pk,
+            "node_id": node_id,
+        }
+        _, task_info = create_async_task(
+            "delete-node", self.user, apply_async=False, **task_args
+        )
+        self.assertFalse("error" in task_info.metadata)
+
     def test_get_or_create_task(self):
         expected_task = Task.objects.create(
             task_type="progress-test",