Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

List of the most important changes for each release.

## 0.8.12
- Fixes issue where dirty-bit isn't updated when calling save on a syncable model with `update_fields`

## 0.8.11
- Adds additional `deserialization_exception` field to `Store` model to track the fully qualified exception path

Expand Down
2 changes: 1 addition & 1 deletion morango/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.8.11"
__version__ = "0.8.12"
7 changes: 7 additions & 0 deletions morango/models/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,13 @@ def save(self, update_dirty_bit_to=True, *args, **kwargs):
self._morango_dirty_bit = True
elif not update_dirty_bit_to:
self._morango_dirty_bit = False

# ensure the dirty bit field is in the fields to update if present, to keep it in sync
if update_dirty_bit_to is not None and kwargs.get("update_fields") is not None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: The update_dirty_bit_to is not None guard correctly handles the opt-out case — callers who pass None to suppress all dirty-bit handling also suppress the update_fields injection, which is exactly right.

kwargs["update_fields"] = set(kwargs["update_fields"]) | {
"_morango_dirty_bit"
}

super(SyncableModel, self).save(*args, **kwargs)

def delete(
Expand Down
9 changes: 9 additions & 0 deletions tests/testapp/tests/integration/test_syncing_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ def test_syncable_save(self):
user.save(update_dirty_bit_to=None)
self.assertTrue(MyUser.objects.first()._morango_dirty_bit)

def test_syncable_save_with_update_fields_persists_dirty_bit(self):
user = MyUser.objects.first()
user.save(update_dirty_bit_to=False)
self.assertFalse(MyUser.objects.first()._morango_dirty_bit)

user.username = "updated-name"
user.save(update_fields=["username"])
self.assertTrue(MyUser.objects.first()._morango_dirty_bit)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This test covers the common case well. Consider adding a second assertion for save(update_dirty_bit_to=None, update_fields=["username"]) to verify that the dirty bit is NOT updated in the DB when callers explicitly opt out — the fix handles it correctly, but a test would prevent regression if the guard condition is ever changed.


def test_syncing_objects_manager_with_custom_default_manager(self):
"""Test that syncing_objects manager includes all objects even when default manager filters them out"""
# Create some test objects
Expand Down
Loading