From d6432fbcb886a8d900b2b24df85512371fa78d80 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 24 Jul 2024 15:58:32 -0400 Subject: [PATCH] Relax requirement for Jobs to reference a specific object --- .../0012_job_object_type_optional.py | 24 ++++++++++++++++++ netbox/core/models/jobs.py | 14 ++++++++--- netbox/utilities/jobs.py | 25 +++---------------- 3 files changed, 37 insertions(+), 26 deletions(-) create mode 100644 netbox/core/migrations/0012_job_object_type_optional.py diff --git a/netbox/core/migrations/0012_job_object_type_optional.py b/netbox/core/migrations/0012_job_object_type_optional.py new file mode 100644 index 000000000..3c6664afc --- /dev/null +++ b/netbox/core/migrations/0012_job_object_type_optional.py @@ -0,0 +1,24 @@ +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('core', '0011_move_objectchange'), + ] + + operations = [ + migrations.AlterField( + model_name='job', + name='object_type', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='jobs', + to='contenttypes.contenttype' + ), + ), + ] diff --git a/netbox/core/models/jobs.py b/netbox/core/models/jobs.py index 14f32f447..07442e3a1 100644 --- a/netbox/core/models/jobs.py +++ b/netbox/core/models/jobs.py @@ -31,6 +31,8 @@ class Job(models.Model): to='contenttypes.ContentType', related_name='jobs', on_delete=models.CASCADE, + blank=True, + null=True ) object_id = models.PositiveBigIntegerField( blank=True, @@ -197,13 +199,13 @@ class Job(models.Model): job_end.send(self) @classmethod - def enqueue(cls, func, instance, name='', user=None, schedule_at=None, interval=None, run_now=False, **kwargs): + def enqueue(cls, func, instance=None, name='', user=None, schedule_at=None, interval=None, run_now=False, **kwargs): """ Create a Job instance and enqueue a job using the given callable Args: func: The callable object to be enqueued for execution - instance: The NetBox object to which this job pertains + instance: The NetBox object to which this job pertains (optional) name: Name for the job (optional) user: The user responsible for running the job schedule_at: Schedule the job to be executed at the passed date and time @@ -211,13 +213,17 @@ class Job(models.Model): run_now: Run the job immediately without scheduling it in the background. Should be used for interactive management commands only. """ - object_type = ObjectType.objects.get_for_model(instance, for_concrete_model=False) + if instance: + object_type = ObjectType.objects.get_for_model(instance, for_concrete_model=False) + object_id = instance.pk + else: + object_type = object_id = None rq_queue_name = get_queue_for_model(object_type.model) queue = django_rq.get_queue(rq_queue_name) status = JobStatusChoices.STATUS_SCHEDULED if schedule_at else JobStatusChoices.STATUS_PENDING job = Job.objects.create( object_type=object_type, - object_id=instance.pk, + object_id=object_id, name=name, status=status, scheduled=schedule_at, diff --git a/netbox/utilities/jobs.py b/netbox/utilities/jobs.py index 94bcbeb9f..f083c8ef8 100644 --- a/netbox/utilities/jobs.py +++ b/netbox/utilities/jobs.py @@ -161,30 +161,11 @@ class SystemJob(ScheduledJob): for system background tasks. The main use case for this method is to schedule jobs programmatically instead of using user events, e.g. to start - jobs when the plugin is loaded in NetBox. For this purpose, the `setup()` method can be used to setup a new schedule - outside of the request-response cycle. It will register the new schedule right after all plugins are loaded and the - database is connected. Then `schedule()` will take care of scheduling a single job at a time. + jobs when the plugin is loaded in NetBox. For this purpose, the `setup()` method can be used to set up a new + schedule outside the request-response cycle. It will register the new schedule right after all plugins are loaded + and the database is connected. Then `schedule()` will take care of scheduling a single job at a time. """ - @classmethod - def enqueue(cls, *args, **kwargs): - kwargs.pop('instance', None) - return super().enqueue(instance=Job(), *args, **kwargs) - - @classmethod - def schedule(cls, *args, **kwargs): - kwargs.pop('instance', None) - return super().schedule(instance=Job(), *args, **kwargs) - - @classmethod - def handle(cls, job, *args, **kwargs): - # A job requires a related object to be handled, or internal methods will fail. To avoid adding an extra model - # for this, the existing job object is used as a reference. This is not ideal, but it works for this purpose. - job.object = job - job.object_id = None # Hide changes from UI - - super().handle(job, *args, **kwargs) - @classmethod def setup(cls, *args, **kwargs): """