From 0f6b4769045a67a8de7dee75bbe101842f52dd07 Mon Sep 17 00:00:00 2001 From: Federico Capoano Date: Fri, 27 Mar 2026 21:02:19 -0300 Subject: [PATCH] [fix] Corrected initial field value assignment in AbstractDevice class Fixed incorrect initialization of `_initial_` values when a Device instance is loaded with deferred fields, which could break change detection logic. [backport 1.2] --- openwisp_controller/config/base/device.py | 6 +++--- openwisp_controller/config/tests/test_device.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/openwisp_controller/config/base/device.py b/openwisp_controller/config/base/device.py index 003fe862e..bbd3d2039 100644 --- a/openwisp_controller/config/base/device.py +++ b/openwisp_controller/config/base/device.py @@ -325,9 +325,9 @@ def _get_initial_values_for_checked_fields(self): if not present_values: return self.refresh_from_db(fields=present_values.keys()) - for field in self._changed_checked_fields: - setattr(self, f"_initial_{field}", field) - setattr(self, field, present_values[field]) + for field, value in present_values.items(): + setattr(self, f"_initial_{field}", getattr(self, field)) + setattr(self, field, value) def _check_name_changed(self): if self._initial_name == models.DEFERRED: diff --git a/openwisp_controller/config/tests/test_device.py b/openwisp_controller/config/tests/test_device.py index eb053f237..350fc15e5 100644 --- a/openwisp_controller/config/tests/test_device.py +++ b/openwisp_controller/config/tests/test_device.py @@ -529,6 +529,20 @@ def test_device_field_changed_checks(self): with self.assertNumQueries(3): device._check_changed_fields() + def test_deferred_fields_populated_correctly(self): + device = self._create_device( + name="deferred-test", + management_ip="10.0.0.1", + ) + # Load the instance with deferred fields omitted + device = Device.objects.only("id").get(pk=device.pk) + device.management_ip = "10.0.0.55" + # Saving the device object will populate the deferred fields + device.save() + # Ensure `_initial_` contains the actual value, not the field name + self.assertEqual(getattr(device, "_initial_management_ip"), "10.0.0.55") + self.assertNotEqual(getattr(device, "_initial_management_ip"), "management_ip") + def test_exceed_organization_device_limit(self): org = self._get_org() org.config_limits.device_limit = 1