Skip to content

Commit dfb443e

Browse files
committed
Delete and retry tasks through Django Admin
1 parent 20dab41 commit dfb443e

File tree

3 files changed

+61
-6
lines changed

3 files changed

+61
-6
lines changed

django_tasks/backends/database/admin.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
11
from typing import List, Optional
22

33
from django.contrib import admin
4+
from django.db.models import QuerySet
45
from django.http import HttpRequest
56

7+
from django_tasks.task import ResultStatus
8+
69
from .models import DBTaskResult
710

811

12+
def reenqueue(
13+
modeladmin: admin.ModelAdmin,
14+
request: HttpRequest,
15+
queryset: QuerySet[DBTaskResult],
16+
) -> None:
17+
tasks = queryset.update(status=ResultStatus.NEW)
18+
modeladmin.message_user(request, f"Rescheduled {tasks} tasks.", "SUCCESS")
19+
20+
21+
def duplicate(
22+
modeladmin: admin.ModelAdmin,
23+
request: HttpRequest,
24+
queryset: QuerySet[DBTaskResult],
25+
) -> None:
26+
tasks = DBTaskResult.objects.bulk_create(
27+
old_task.duplicate() for old_task in queryset
28+
)
29+
modeladmin.message_user(request, f"Rescheduled {tasks} tasks.", "SUCCESS")
30+
31+
932
@admin.register(DBTaskResult)
1033
class DBTaskResultAdmin(admin.ModelAdmin):
1134
list_display = (
@@ -20,17 +43,13 @@ class DBTaskResultAdmin(admin.ModelAdmin):
2043
)
2144
list_filter = ("status", "priority", "queue_name")
2245
ordering = ["-enqueued_at"]
46+
actions = [reenqueue, duplicate]
2347

2448
def has_add_permission(
2549
self, request: HttpRequest, obj: Optional[DBTaskResult] = None
2650
) -> bool:
2751
return False
2852

29-
def has_delete_permission(
30-
self, request: HttpRequest, obj: Optional[DBTaskResult] = None
31-
) -> bool:
32-
return False
33-
3453
def has_change_permission(
3554
self, request: HttpRequest, obj: Optional[DBTaskResult] = None
3655
) -> bool:

django_tasks/backends/database/models.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22
import uuid
3-
from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar
3+
from typing import TYPE_CHECKING, Any, Generic, Optional, Self, TypeVar
44

55
import django
66
from django.core.exceptions import SuspiciousOperation
@@ -167,6 +167,7 @@ def task_result(self) -> "TaskResult[T]":
167167

168168
object.__setattr__(task_result, "_exception_class", exception_class)
169169
object.__setattr__(task_result, "_traceback", self.traceback or None)
170+
object.__setattr__(task_result, "_return_value", self.return_value)
170171

171172
return task_result
172173

@@ -226,3 +227,13 @@ def set_failed(self, exc: BaseException) -> None:
226227
"traceback",
227228
]
228229
)
230+
231+
def duplicate(self) -> Self:
232+
return type(self)(
233+
args_kwargs=self.args_kwargs,
234+
priority=self.priority,
235+
task_path=self.task_path,
236+
queue_name=self.queue_name,
237+
backend_name=self.backend_name,
238+
run_after=self.run_after,
239+
)

tests/tests/test_database_backend.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
from django_tasks import ResultStatus, Task, default_task_backend, tasks
2828
from django_tasks.backends.database import DatabaseBackend
29+
from django_tasks.backends.database.backend import TaskResult
2930
from django_tasks.backends.database.management.commands.prune_db_task_results import (
3031
logger as prune_db_tasks_logger,
3132
)
@@ -757,6 +758,8 @@ def test_worker_with_locked_rows(self) -> None:
757758
}
758759
)
759760
class DatabaseTaskResultTestCase(TransactionTestCase):
761+
run_worker = partial(call_command, "db_worker", verbosity=0, batch=True, interval=0)
762+
760763
def execute_in_new_connection(self, sql: Union[str, QuerySet]) -> Sequence:
761764
if isinstance(sql, QuerySet):
762765
sql = str(sql.query)
@@ -953,6 +956,28 @@ def test_get_locked_with_locked_rows(self) -> None:
953956
finally:
954957
new_connection.close()
955958

959+
def test_duplicate(self) -> None:
960+
result_1 = cast(TaskResult, test_tasks.calculate_meaning_of_life.enqueue())
961+
db_result_1 = result_1.db_result
962+
db_result_2 = result_1.db_result.duplicate()
963+
db_result_2.save()
964+
result_2 = db_result_2.task_result
965+
966+
assert db_result_1.pk != db_result_2.pk
967+
968+
call_command(
969+
"db_worker",
970+
verbosity=0,
971+
batch=True,
972+
interval=0,
973+
startup_delay=False,
974+
)
975+
976+
result_1.refresh()
977+
result_2.refresh()
978+
979+
assert result_1.return_value == result_2.return_value
980+
956981

957982
class ConnectionExclusiveTranscationTestCase(TestCase):
958983
def setUp(self) -> None:

0 commit comments

Comments
 (0)