mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-16 04:02:52 -06:00
Add name attribute for BackgroundJob
Instead of defining individual names on enqueue, BackgroundJob classes can now set a job name in their meta class. This is equivalent to other Django classes and NetBox scripts.
This commit is contained in:
parent
58089c726a
commit
fb75389261
@ -17,19 +17,30 @@ A background job implements a basic [Job](../../models/core/job.md) executor for
|
|||||||
from utilities.jobs import BackgroundJob
|
from utilities.jobs import BackgroundJob
|
||||||
|
|
||||||
class MyTestJob(BackgroundJob):
|
class MyTestJob(BackgroundJob):
|
||||||
|
class Meta:
|
||||||
|
name = "My Test Job"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls, job, *args, **kwargs):
|
def run(cls, job, *args, **kwargs):
|
||||||
obj = job.object
|
obj = job.object
|
||||||
# your logic goes here
|
# your logic goes here
|
||||||
```
|
```
|
||||||
|
|
||||||
You can schedule the background job from within your code (e.g. from a model's `save()` method or a view) by calling `MyTestJob.enqueue()`. This method passes through all arguments to `Job.enqueue()`.
|
You can schedule the background job from within your code (e.g. from a model's `save()` method or a view) by calling `MyTestJob.enqueue()`. This method passes through all arguments to `Job.enqueue()`. However, no `name` argument must be passed, as the background job name will be used instead.
|
||||||
|
|
||||||
::: core.models.Job.enqueue
|
::: core.models.Job.enqueue
|
||||||
|
|
||||||
|
#### Job Attributes
|
||||||
|
|
||||||
|
Background job attributes are defined under a class named `Meta` within the job. These are optional, but encouraged.
|
||||||
|
|
||||||
|
##### `name`
|
||||||
|
|
||||||
|
This is the human-friendly names of your background job. If omitted, the class name will be used.
|
||||||
|
|
||||||
#### Scheduled Jobs
|
#### Scheduled Jobs
|
||||||
|
|
||||||
As described above, jobs can be scheduled for immediate execution or at any later time using the `enqueue()` method. However, for management purposes, the `enqueue_once()` method allows a job to be scheduled exactly once avoiding duplicates. If a job is already scheduled for a particular instance, a second one won't be scheduled, respecting thread safety. An example use case would be to schedule a periodic task that is bound to an instance in general, but not to any event of that instance (such as updates). The parameters of the `enqueue_once()` method are identical to those of `enqueue()`. Note that this class doesn't allow you to pass the `name` parameter, but uses a generic name instead.
|
As described above, jobs can be scheduled for immediate execution or at any later time using the `enqueue()` method. However, for management purposes, the `enqueue_once()` method allows a job to be scheduled exactly once avoiding duplicates. If a job is already scheduled for a particular instance, a second one won't be scheduled, respecting thread safety. An example use case would be to schedule a periodic task that is bound to an instance in general, but not to any event of that instance (such as updates). The parameters of the `enqueue_once()` method are identical to those of `enqueue()`.
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
It is not forbidden to `enqueue()` additional jobs while an interval schedule is active. An example use of this would be to schedule a periodic daily synchronization, but also trigger additional synchronizations on demand when the user presses a button.
|
It is not forbidden to `enqueue()` additional jobs while an interval schedule is active. An example use of this would be to schedule a periodic daily synchronization, but also trigger additional synchronizations on demand when the user presses a button.
|
||||||
|
@ -14,6 +14,9 @@ class SyncDataSourceJob(BackgroundJob):
|
|||||||
Call sync() on a DataSource.
|
Call sync() on a DataSource.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = 'Synchronization'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls, job, *args, **kwargs):
|
def run(cls, job, *args, **kwargs):
|
||||||
datasource = DataSource.objects.get(pk=job.object_id)
|
datasource = DataSource.objects.get(pk=job.object_id)
|
||||||
|
@ -21,6 +21,11 @@ class ScriptJob(BackgroundJob):
|
|||||||
exists outside the Script class to ensure it cannot be overridden by a script author.
|
exists outside the Script class to ensure it cannot be overridden by a script author.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
# An explicit job name is not set because it doesn't make sense in this context. Currently, there's no scenario
|
||||||
|
# where jobs other than this one are used. Therefore, it is hidden, resulting in a cleaner job table overview.
|
||||||
|
name = ''
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def run_script(script, job, request, data, commit):
|
def run_script(script, job, request, data, commit):
|
||||||
"""
|
"""
|
||||||
|
@ -3,6 +3,7 @@ from abc import ABC, abstractmethod
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from django.db.backends.signals import connection_created
|
from django.db.backends.signals import connection_created
|
||||||
|
from django.utils.functional import classproperty
|
||||||
from django_pglocks import advisory_lock
|
from django_pglocks import advisory_lock
|
||||||
from rq.timeouts import JobTimeoutException
|
from rq.timeouts import JobTimeoutException
|
||||||
|
|
||||||
@ -24,6 +25,13 @@ class BackgroundJob(ABC):
|
|||||||
and scheduling recurring jobs.
|
and scheduling recurring jobs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classproperty
|
||||||
|
def name(cls):
|
||||||
|
return getattr(cls.Meta, 'name', cls.__name__)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def run(cls, *args, **kwargs):
|
def run(cls, *args, **kwargs):
|
||||||
@ -74,7 +82,7 @@ class BackgroundJob(ABC):
|
|||||||
return Job.objects.filter(
|
return Job.objects.filter(
|
||||||
object_type=object_type,
|
object_type=object_type,
|
||||||
object_id=instance.pk,
|
object_id=instance.pk,
|
||||||
name=cls.__name__,
|
name=cls.name,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -85,7 +93,7 @@ class BackgroundJob(ABC):
|
|||||||
This method is a wrapper of `Job.enqueue()` using `handle()` as function callback. See its documentation for
|
This method is a wrapper of `Job.enqueue()` using `handle()` as function callback. See its documentation for
|
||||||
parameters.
|
parameters.
|
||||||
"""
|
"""
|
||||||
return Job.enqueue(cls.handle, *args, **kwargs)
|
return Job.enqueue(cls.handle, name=cls.name, *args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@advisory_lock(ADVISORY_LOCK_KEYS['job-schedules'])
|
@advisory_lock(ADVISORY_LOCK_KEYS['job-schedules'])
|
||||||
@ -115,7 +123,7 @@ class BackgroundJob(ABC):
|
|||||||
return job
|
return job
|
||||||
job.delete()
|
job.delete()
|
||||||
|
|
||||||
return cls.enqueue(instance=instance, name=cls.__name__, interval=interval, *args, **kwargs)
|
return cls.enqueue(instance=instance, interval=interval, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SystemJob(BackgroundJob):
|
class SystemJob(BackgroundJob):
|
||||||
|
@ -29,6 +29,22 @@ class BackgroundJobTestCase(TestCase):
|
|||||||
return timezone.now() + timedelta(weeks=1)
|
return timezone.now() + timedelta(weeks=1)
|
||||||
|
|
||||||
|
|
||||||
|
class BackgroundJobTest(BackgroundJobTestCase):
|
||||||
|
"""
|
||||||
|
Test internal logic of `BackgroundJob`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_name_default(self):
|
||||||
|
self.assertEqual(TestBackgroundJob.name, TestBackgroundJob.__name__)
|
||||||
|
|
||||||
|
def test_name_set(self):
|
||||||
|
class NamedBackgroundJob(TestBackgroundJob):
|
||||||
|
class Meta:
|
||||||
|
name = 'TestName'
|
||||||
|
|
||||||
|
self.assertEqual(NamedBackgroundJob.name, 'TestName')
|
||||||
|
|
||||||
|
|
||||||
class EnqueueTest(BackgroundJobTestCase):
|
class EnqueueTest(BackgroundJobTestCase):
|
||||||
"""
|
"""
|
||||||
Test enqueuing of `BackgroundJob`.
|
Test enqueuing of `BackgroundJob`.
|
||||||
@ -40,7 +56,7 @@ class EnqueueTest(BackgroundJobTestCase):
|
|||||||
job = TestBackgroundJob.enqueue(instance, schedule_at=self.get_schedule_at())
|
job = TestBackgroundJob.enqueue(instance, schedule_at=self.get_schedule_at())
|
||||||
|
|
||||||
self.assertIsInstance(job, Job)
|
self.assertIsInstance(job, Job)
|
||||||
self.assertEqual(Job.objects.count(), i)
|
self.assertEqual(TestBackgroundJob.get_jobs(instance).count(), i)
|
||||||
|
|
||||||
def test_enqueue_once(self):
|
def test_enqueue_once(self):
|
||||||
job = TestBackgroundJob.enqueue_once(instance=Job(), schedule_at=self.get_schedule_at())
|
job = TestBackgroundJob.enqueue_once(instance=Job(), schedule_at=self.get_schedule_at())
|
||||||
|
Loading…
Reference in New Issue
Block a user