diff --git a/AKModel/availability/forms.py b/AKModel/availability/forms.py index 3c68c56b03b1ecaaea4f91aafb464883e73a4ea8..c99ca202b6ab8c8fe70c4bacc949040d48c5f0d4 100644 --- a/AKModel/availability/forms.py +++ b/AKModel/availability/forms.py @@ -7,6 +7,7 @@ import json from django import forms from django.db import transaction +from django.db.models.signals import post_save from django.utils.dateparse import parse_datetime from django.utils.translation import gettext_lazy as _ @@ -142,11 +143,16 @@ class AvailabilitiesFormMixin(forms.Form): for avail in availabilities: setattr(avail, reference_name, instance.id) - def _replace_availabilities(self, instance, availabilities): + def _replace_availabilities(self, instance, availabilities: [Availability]): with transaction.atomic(): # TODO: do not recreate objects unnecessarily, give the client the IDs, so we can track modifications and leave unchanged objects alone instance.availabilities.all().delete() Availability.objects.bulk_create(availabilities) + # Trigger post save signal manually to make sure constraints are updated accordingly + # Doing this one time is sufficient, since this will nevertheless update all availability constraint + # violations of the corresponding AK + if len(availabilities) > 0: + post_save.send(Availability, instance=availabilities[0], created=True) def save(self, *args, **kwargs): instance = super().save(*args, **kwargs) diff --git a/AKScheduling/models.py b/AKScheduling/models.py index bfdb1e16d54a844501082704704239f34d62d429..cabf78e16e38fd5e14b4383958244596fc935ddd 100644 --- a/AKScheduling/models.py +++ b/AKScheduling/models.py @@ -1,4 +1,4 @@ -from django.db.models.signals import post_save, m2m_changed +from django.db.models.signals import post_save, m2m_changed, pre_delete from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ @@ -551,6 +551,19 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs): update_constraint_violations(new_violations, existing_violations_to_check) +@receiver(pre_delete, sender=AKSlot) +def akslot_deleted_handler(sender, instance: AKSlot, **kwargs): + # Manually clean up or remove constraint violations that belong to this slot since there is no cascade deletion + # for many2many relationships. Explicitly listening for AK deletion signals is not necessary since they will + # transitively trigger this signal and we always set both AK and AKSlot references in a constraint violation + print(f"{instance} deleted") + + for cv in instance.constraintviolation_set.all(): + # Make sure not delete CVs that e.g., show three parallel slots in a single room + if cv.ak_slots.count() <= 2: + cv.delete() + + @receiver(post_save, sender=Room) def room_changed_handler(sender, instance: Room, **kwargs): # Changes might affect: Room size