Skip to content

Commit bb926b4

Browse files
author
Tinde von Wachenfeldt
committed
Block edits on activation date and permission groups in pools with registrations
1 parent 7801052 commit bb926b4

File tree

5 files changed

+108
-32
lines changed

5 files changed

+108
-32
lines changed

lego/apps/events/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,9 @@ def decrement(self) -> Pool:
849849
self.save(update_fields=["counter"])
850850
return self
851851

852+
def permission_group_ids(self) -> set[int]:
853+
return set(self.permission_groups.values_list("id", flat=True))
854+
852855
@abakus_cached_property
853856
def all_permission_groups(self):
854857
groups = self.permission_groups.all()

lego/apps/events/serializers/events.py

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -435,9 +435,20 @@ def create(self, validated_data):
435435
with transaction.atomic():
436436
event = super().create(validated_data)
437437
for pool in pools:
438-
permission_groups = pool.pop("permission_groups")
439-
created_pool = Pool.objects.create(event=event, **pool)
440-
created_pool.permission_groups.set(permission_groups)
438+
permission_groups = pool.get("permission_groups", [])
439+
pool_data = {
440+
"name": pool.get("name"),
441+
"capacity": pool.get("capacity"),
442+
"activation_date": pool.get("activation_date"),
443+
"permission_groups": [
444+
getattr(gr, "id", gr) for gr in permission_groups
445+
],
446+
}
447+
pool_serializer = PoolCreateAndUpdateSerializer(
448+
data=pool_data, context={**self.context, "event": event}
449+
)
450+
pool_serializer.is_valid(raise_exception=True)
451+
pool_serializer.save()
441452
return event
442453

443454
def update(self, instance, validated_data):
@@ -454,24 +465,40 @@ def update(self, instance, validated_data):
454465
pools[0]["capacity"] = 0
455466
with transaction.atomic():
456467
if pools is not None:
457-
existing_pools = list(instance.pools.all().values_list("id", flat=True))
468+
existing_ids = set(instance.pools.values_list("id", flat=True))
458469
for pool in pools:
459-
pool_id = pool.get("id", None)
460-
if pool_id in existing_pools:
461-
existing_pools.remove(pool_id)
462-
permission_groups = pool.pop("permission_groups")
463-
created_pool = Pool.objects.update_or_create(
464-
event=instance,
465-
id=pool_id,
466-
defaults={
467-
"name": pool.get("name"),
468-
"capacity": pool.get("capacity", 0),
469-
"activation_date": pool.get("activation_date"),
470-
},
471-
)[0]
472-
created_pool.permission_groups.set(permission_groups)
473-
for pool_id in existing_pools:
474-
Pool.objects.get(id=pool_id).delete()
470+
pool_id = pool.get("id")
471+
pool_instance = (
472+
Pool.objects.filter(id=pool_id, event=instance)
473+
.select_for_update()
474+
.first()
475+
if pool_id
476+
else None
477+
)
478+
if pool_instance:
479+
existing_ids.discard(pool_id)
480+
permission_groups = pool.get("permission_groups", [])
481+
pool_data = {
482+
"name": pool.get("name"),
483+
"capacity": pool.get("capacity"),
484+
"activation_date": pool.get("activation_date"),
485+
"permission_groups": [
486+
getattr(gr, "id", gr) for gr in permission_groups
487+
],
488+
}
489+
pool_serializer = PoolCreateAndUpdateSerializer(
490+
instance=pool_instance,
491+
data=pool_data,
492+
context={**self.context, "event": instance},
493+
partial=True,
494+
)
495+
pool_serializer.is_valid(raise_exception=True)
496+
pool_serializer.save()
497+
if existing_ids:
498+
for pool_obj in Pool.objects.filter(
499+
event=instance, id__in=existing_ids
500+
).iterator():
501+
pool_obj.delete()
475502
return super().update(instance, validated_data)
476503

477504

lego/apps/events/serializers/pools.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from rest_framework import serializers
22

33
from lego.apps.events.fields import RegistrationCountField
4-
from lego.apps.events.models import Event, Pool
4+
from lego.apps.events.models import Pool
55
from lego.apps.events.serializers.registrations import (
66
RegistrationPaymentReadSerializer,
77
RegistrationReadDetailedAllergiesSerializer,
@@ -29,14 +29,6 @@ class Meta:
2929
)
3030
read_only = True
3131

32-
def create(self, validated_data):
33-
event = Event.objects.get(pk=self.context["view"].kwargs["event_pk"])
34-
permission_groups = validated_data.pop("permission_groups")
35-
pool = Pool.objects.create(event=event, **validated_data)
36-
pool.permission_groups.set(permission_groups)
37-
38-
return pool
39-
4032

4133
class PoolReadAuthSerializer(PoolReadSerializer):
4234
registrations = serializers.SerializerMethodField()
@@ -90,10 +82,32 @@ class Meta:
9082
"registrations": {"read_only": True},
9183
}
9284

85+
def validate(self, attrs):
86+
instance = getattr(self, "instance", None)
87+
if not instance or instance.registration_count < 1:
88+
return attrs
89+
90+
if "permission_groups" in attrs:
91+
new_ids = {getattr(gr, "id", gr) for gr in attrs["permission_groups"]}
92+
old_ids = instance.permission_group_ids()
93+
if new_ids != old_ids:
94+
raise serializers.ValidationError(
95+
{
96+
"permission_groups": "Group edits are disabled for pools with registrations."
97+
}
98+
)
99+
if "activation_date" in attrs:
100+
if attrs["activation_date"] != instance.activation_date:
101+
raise serializers.ValidationError(
102+
{
103+
"activation_date": "Time travel is disabled for pools with registrations."
104+
}
105+
)
106+
return attrs
107+
93108
def create(self, validated_data):
94-
event = Event.objects.get(pk=self.context["view"].kwargs["event_pk"])
109+
event = validated_data.pop("event", None) or self.context.get("event")
95110
permission_groups = validated_data.pop("permission_groups")
96111
pool = Pool.objects.create(event=event, **validated_data)
97112
pool.permission_groups.set(permission_groups)
98-
99113
return pool

lego/apps/events/tests/test_events_api.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,31 @@ def test_delete_pool_without_registrations_as_abakus(self):
10001000
pool_response = self.client.delete(_get_pools_detail_url(1, pool.id))
10011001
self.assertEqual(pool_response.status_code, status.HTTP_403_FORBIDDEN)
10021002

1003+
def test_patch_permission_groups_in_pool_with_registrations(self):
1004+
"""Test that change of permission group is not possible in pool with registrations"""
1005+
AbakusGroup.objects.get(name="Bedkom").add_user(self.abakus_user)
1006+
self.client.force_authenticate(self.abakus_user)
1007+
new_group = AbakusGroup.objects.get(name="Webkom")
1008+
pool_response = self.client.patch(
1009+
_get_pools_detail_url(1, 1),
1010+
{"permissionGroups": [new_group.id]},
1011+
format="json",
1012+
)
1013+
self.assertEqual(pool_response.status_code, status.HTTP_400_BAD_REQUEST)
1014+
self.assertIn("permissionGroups", pool_response.json())
1015+
1016+
def test_patch_activation_date_in_pool_with_registrations(self):
1017+
"""Test that change of activation date is not possible in pool with registrations"""
1018+
AbakusGroup.objects.get(name="Bedkom").add_user(self.abakus_user)
1019+
self.client.force_authenticate(self.abakus_user)
1020+
pool_response = self.client.patch(
1021+
_get_pools_detail_url(1, 1),
1022+
{"activationDate": timezone.now().isoformat()},
1023+
format="json",
1024+
)
1025+
self.assertEqual(pool_response.status_code, status.HTTP_400_BAD_REQUEST)
1026+
self.assertIn("activationDate", pool_response.json())
1027+
10031028

10041029
@mock.patch("lego.apps.events.views.verify_captcha", return_value=True)
10051030
class RegistrationsTransactionTestCase(BaseAPITransactionTestCase):

lego/apps/events/views.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,11 +346,18 @@ class PoolViewSet(
346346
serializer_class = PoolCreateAndUpdateSerializer
347347

348348
def get_queryset(self):
349-
event_id = self.kwargs.get("event_pk", None)
349+
event_id = self.kwargs.get("event_pk")
350350
return Pool.objects.filter(event=event_id).prefetch_related(
351351
"permission_groups", "registrations"
352352
)
353353

354+
def get_serializer_context(self):
355+
context = super().get_serializer_context()
356+
event_id = self.kwargs.get("event_pk")
357+
if event_id:
358+
context["event"] = get_object_or_404(Event, pk=event_id)
359+
return context
360+
354361
def destroy(self, request, *args, **kwargs):
355362
try:
356363
return super().destroy(request, *args, **kwargs)

0 commit comments

Comments
 (0)