Make save method atomic

This commit is contained in:
Oliver Walters
2026-06-13 11:08:13 +00:00
parent 5b18401684
commit fdef49c25e
2 changed files with 27 additions and 7 deletions
@@ -121,4 +121,13 @@ class Migration(migrations.Migration):
related_name='images', related_name='images',
), ),
), ),
# Add constraint to ensure that only one 'primary' note exists per model instance
migrations.AddConstraint(
model_name="note",
constraint=models.UniqueConstraint(
condition=models.Q(("primary", True)),
fields=("model_type", "model_id"),
name="unique_primary_note_per_model",
),
),
] ]
+18 -7
View File
@@ -3000,21 +3000,34 @@ class Note(
verbose_name = _('Note') verbose_name = _('Note')
verbose_name_plural = _('Notes') verbose_name_plural = _('Notes')
constraints = [
models.UniqueConstraint(
fields=['model_type', 'model_id'],
condition=models.Q(primary=True),
name='unique_primary_note_per_model',
)
]
@staticmethod @staticmethod
def get_api_url() -> str: def get_api_url() -> str:
"""Return the API URL associated with the Parameter model.""" """Return the API URL associated with the Parameter model."""
return reverse('api-note-list') return reverse('api-note-list')
@transaction.atomic
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
"""Perform custom save checks before saving a Note instance.""" """Perform custom save checks before saving a Note instance."""
self.check_save() self.check_save()
others = Note.objects.filter( # Lock sibling notes to serialize concurrent primary-flag updates
model_type=self.model_type, model_id=self.model_id siblings = (
).exclude(pk=self.pk) Note.objects
.select_for_update()
.filter(model_type=self.model_type, model_id=self.model_id)
.exclude(pk=self.pk)
)
# If this is the *only* note for this model instance, then set it as the primary note # If this is the *only* note for this model instance, then set it as the primary note
if not others.exists(): if not siblings.exists():
self.primary = True self.primary = True
self.clean() self.clean()
@@ -3023,9 +3036,7 @@ class Note(
# Once this note is saved, mark other notes as non-primary # Once this note is saved, mark other notes as non-primary
if self.primary: if self.primary:
Note.objects.filter( siblings.update(primary=False)
model_type=self.model_type, model_id=self.model_id
).exclude(pk=self.pk).update(primary=False)
self.cleanup_images() self.cleanup_images()