diff --git a/apps/api/plane/db/mixins.py b/apps/api/plane/db/mixins.py index be5613b613f..e43a46e2947 100644 --- a/apps/api/plane/db/mixins.py +++ b/apps/api/plane/db/mixins.py @@ -188,3 +188,30 @@ def old_values(self) -> dict[str, Any]: all non-deferred fields). """ return self._original_values + + def save(self, *args: Any, **kwargs: Any) -> None: + """ + Override save to automatically capture changed fields and reset tracking. + + Before saving, the current changed_fields are captured and stored in + _changes_on_save. After saving, the tracked fields are reset so + that subsequent saves correctly detect changes relative to the last + saved state, not the original load-time state. + + Models that need to access the changed fields after save (e.g., for + syncing related models) can use self._changes_on_save. + """ + self._changes_on_save = self.changed_fields + super().save(*args, **kwargs) + self._reset_tracked_fields() + + def _reset_tracked_fields(self) -> None: + """ + Reset the tracked field values to the current state. + + This is called automatically after save() to ensure that subsequent + saves correctly detect changes relative to the last saved state, + rather than the original load-time state. + """ + self._original_values = {} + self._track_fields() diff --git a/apps/api/plane/db/models/issue.py b/apps/api/plane/db/models/issue.py index d3377f0ad37..05bb2fe5b91 100644 --- a/apps/api/plane/db/models/issue.py +++ b/apps/api/plane/db/models/issue.py @@ -513,10 +513,12 @@ def save(self, *args, **kwargs): "comment_json": "description_json", } + # Use _changes_on_save which is captured by ChangeTrackerMixin.save() + # before the tracked fields are reset changed_fields = { desc_field: getattr(self, comment_field) for comment_field, desc_field in field_mapping.items() - if self.has_changed(comment_field) + if comment_field in self._changes_on_save } # Update description only if comment fields changed