diff --git a/openwisp_monitoring/check/base/models.py b/openwisp_monitoring/check/base/models.py index ed1af76f4..f7ffbaef6 100644 --- a/openwisp_monitoring/check/base/models.py +++ b/openwisp_monitoring/check/base/models.py @@ -74,26 +74,20 @@ def clean(self): self.check_instance.validate() def full_clean(self, *args, **kwargs): - # The name of the check will be the same as the - # 'check_type' chosen by the user when the - # name field is empty (useful for CheckInline) if not self.name: self.name = self.get_check_type_display() return super().full_clean(*args, **kwargs) @cached_property def check_class(self): - """Returns the check class.""" return import_string(self.check_type) @cached_property def check_instance(self): - """Returns the check class instance.""" check_class = self.check_class return check_class(check=self, params=self.params) def perform_check(self, store=True): - """Initializes check instance and calls the check method.""" if ( hasattr(self.content_object, "is_deactivated") and self.content_object.is_deactivated() @@ -110,20 +104,42 @@ def perform_check_delayed(self, duration=0): perform_check.apply_async(args=[self.id], countdown=duration) @classmethod - def auto_create_check_receiver(cls, created, **kwargs): + def auto_create_check_receiver(cls, sender, instance, created, **kwargs): if not created: return - transaction_on_commit(lambda: _auto_check_receiver(created=created, **kwargs)) + + transaction_on_commit( + lambda: _auto_check_receiver(sender=sender, instance=instance) + ) def _auto_check_receiver(sender, instance, **kwargs): + from swapper import load_model + + Check = load_model("check", "Check") + model = sender.__name__.lower() app_label = sender._meta.app_label object_id = str(instance.pk) + ct = ContentType.objects.get_for_model(instance) + + # Prefetch existing check_types for this object to ensure idempotency + # This avoids creating duplicate checks when the signal is triggered multiple times + existing_check_types = set( + Check.objects.filter( + content_type=ct, + object_id=object_id, + ).values_list("check_type", flat=True) + ) + for class_string, name, auto_create_setting in app_settings.CHECK_CLASSES: if not getattr(app_settings, auto_create_setting): continue + + if class_string in existing_check_types: + continue + auto_create_check.delay( model=model, app_label=app_label,