Repository: nautobot/nautobot

Issue:
The command 'nautobot-server celery result task_id' says "Cannot resolve keyword 'task_id' into field"
### Environment
* Nautobot version : v2.1.4
* Python version: 3.10.12
* Database platform, version: psql (PostgreSQL) 14.11 (Ubuntu 14.11-0ubuntu0.22.04.1)
* Middleware(s): NA

### Steps to Reproduce
1. open terminal as nautobot user 
2. enter command :- 'nautobot-server celery result 505........89f' where the last argument would be task_id

### Expected Behavior
I expect to see the result of the task whose task_id is mentioned.

### Observed Behavior
Instead the following exception is returned
```
Traceback (most recent call last):
  File "/opt/nautobot/bin/nautobot-server", line 8, in <module>
    sys.exit(main())
  File "/opt/nautobot/lib/python3.10/site-packages/nautobot/core/cli/__init__.py", line 52, in main
    run_app(
  File "/opt/nautobot/lib/python3.10/site-packages/nautobot/core/runner/runner.py", line 297, in run_app
    management.execute_from_command_line([runner_name, command, *command_args])
  File "/opt/nautobot/lib/python3.10/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/opt/nautobot/lib/python3.10/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/opt/nautobot/lib/python3.10/site-packages/nautobot/core/management/commands/celery.py", line 23, in run_from_argv
    celery_main.main()
  File "/opt/nautobot/lib/python3.10/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
  File "/opt/nautobot/lib/python3.10/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/opt/nautobot/lib/python3.10/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/opt/nautobot/lib/python3.10/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/opt/nautobot/lib/python3.10/site-packages/click/decorators.py", line 33, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/opt/nautobot/lib/python3.10/site-packages/celery/bin/base.py", line 134, in caller
    return f(ctx, *args, **kwargs)
  File "/opt/nautobot/lib/python3.10/site-packages/celery/bin/result.py", line 27, in result
    value = task_result.traceback if traceback else task_result.get()
  File "/opt/nautobot/lib/python3.10/site-packages/celery/result.py", line 251, in get
    return self.backend.wait_for_pending(
  File "/opt/nautobot/lib/python3.10/site-packages/celery/backends/base.py", line 755, in wait_for_pending
    meta = self.wait_for(
  File "/opt/nautobot/lib/python3.10/site-packages/celery/backends/base.py", line 782, in wait_for
    meta = self.get_task_meta(task_id)
  File "/opt/nautobot/lib/python3.10/site-packages/celery/backends/base.py", line 608, in get_task_meta
    meta = self._get_task_meta_for(task_id)
  File "/opt/nautobot/lib/python3.10/site-packages/django_celery_results/backends/database.py", line 137, in _get_task_meta_for
    obj = self.TaskModel._default_manager.get_task(task_id)
  File "/opt/nautobot/lib/python3.10/site-packages/django_celery_results/managers.py", line 111, in get_task
    return self.get(task_id=task_id)
  File "/opt/nautobot/lib/python3.10/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/opt/nautobot/lib/python3.10/site-packages/django/db/models/query.py", line 424, in get
    clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs)
  File "/opt/nautobot/lib/python3.10/site-packages/nautobot/core/models/querysets.py", line 92, in filter
    return super().filter(*args, **self.split_composite_key_into_kwargs(composite_key, **kwargs))
  File "/opt/nautobot/lib/python3.10/site-packages/django/db/models/query.py", line 941, in filter
    return self._filter_or_exclude(False, args, kwargs)
  File "/opt/nautobot/lib/python3.10/site-packages/django/db/models/query.py", line 961, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "/opt/nautobot/lib/python3.10/site-packages/django/db/models/query.py", line 968, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "/opt/nautobot/lib/python3.10/site-packages/django/db/models/sql/query.py", line 1416, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "/opt/nautobot/lib/python3.10/site-packages/django/db/models/sql/query.py", line 1435, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "/opt/nautobot/lib/python3.10/site-packages/django/db/models/sql/query.py", line 1309, in build_filter
    lookups, parts, reffed_expression = self.solve_lookup_type(arg)
  File "/opt/nautobot/lib/python3.10/site-packages/django/db/models/sql/query.py", line 1135, in solve_lookup_type
    _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())
  File "/opt/nautobot/lib/python3.10/site-packages/django/db/models/sql/query.py", line 1562, in names_to_path
    raise FieldError("Cannot resolve keyword '%s' into field. "
django.core.exceptions.FieldError: Cannot resolve keyword 'task_id' into field. Choices are: _custom_field_data, celery_kwargs, date_created, date_done, files, id, job_log_entries, job_model, job_model_id, meta, name, result, scheduled_job, scheduled_job_id, status, task_args, task_kwargs, task_name, traceback, user, user_id, worker
```

![image (1)](https://github.com/nautobot/nautobot/assets/124126542/17b835f0-7f9c-4dfc-b541-45da4509db78)
Here is the image which shows usage of the command


Discussion:
Thanks! Looks like this is a mismatch between `django-celery-result` and our implementation. We already have a custom manager subclass of `django-celery-result`'s `TaskResultManager`, but we're not overriding its `get_task()` API. I think if we added an override that mapped `task_id` to our `JobResult.id` field it would resolve this.

Resolving patch:
diff --git a/nautobot/extras/managers.py b/nautobot/extras/managers.py
--- a/nautobot/extras/managers.py
+++ b/nautobot/extras/managers.py
@@ -8,6 +8,20 @@
 
 
 class JobResultManager(BaseManager.from_queryset(RestrictedQuerySet), TaskResultManager):
+    def get_task(self, task_id):
+        """Get result for task by ``task_id``.
+
+        This overloads `TaskResultManager.get_task` provided by `django-celery-results` to manage custom
+        behaviors for integration with Nautobot.
+        """
+        try:
+            return self.get(id=task_id)
+        except self.model.DoesNotExist:
+            if self._last_id == task_id:
+                self.warn_if_repeatable_read()
+            self._last_id = task_id
+            return self.model(id=task_id)
+
     @transaction_retry(max_retries=2)
     def store_result(
         self,


Test patch:
diff --git a/nautobot/extras/tests/test_models.py b/nautobot/extras/tests/test_models.py
--- a/nautobot/extras/tests/test_models.py
+++ b/nautobot/extras/tests/test_models.py
@@ -1811,6 +1811,15 @@ def test_passing_invalid_data_into_job_result(self):
                 JobResult.objects.create(name="ExampleJob2", user=None, result=lambda: 1)
             self.assertEqual(str(err.exception), "Object of type function is not JSON serializable")
 
+    def test_get_task(self):
+        """Assert bug fix for `Cannot resolve keyword 'task_id' into field` #5440"""
+        data = {
+            "output": "valid data",
+        }
+        job_result = JobResult.objects.create(name="ExampleJob1", user=None, result=data)
+
+        self.assertEqual(JobResult.objects.get_task(job_result.pk), job_result)
+
 
 class WebhookTest(ModelTestCases.BaseModelTestCase):
     model = Webhook