mirror of
https://github.com/ae-utbm/sith.git
synced 2025-09-13 19:45:50 +00:00
feat: MembershipQuerySet.editable_by
method
This commit is contained in:
@@ -30,7 +30,8 @@ from django.core.cache import cache
|
||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||
from django.core.validators import RegexValidator, validate_email
|
||||
from django.db import models, transaction
|
||||
from django.db.models import Exists, F, OuterRef, Q
|
||||
from django.db.models import Exists, F, OuterRef, Q, Value
|
||||
from django.db.models.functions import Greatest
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.functional import cached_property
|
||||
@@ -243,6 +244,41 @@ class MembershipQuerySet(models.QuerySet):
|
||||
"""
|
||||
return self.filter(role__gt=settings.SITH_MAXIMUM_FREE_ROLE)
|
||||
|
||||
def editable_by(self, user: User) -> Self:
|
||||
"""Filter Memberships that this user can edit.
|
||||
|
||||
Users with the `club.change_membership` permission can edit all Membership.
|
||||
The other users can end :
|
||||
- their own membership
|
||||
- if they are board members, memberships with a role lower than their own
|
||||
|
||||
For example, let's suppose the following users :
|
||||
- A : board member
|
||||
- B : board member
|
||||
- C : simple member
|
||||
- D : curious
|
||||
|
||||
A will be able to end the memberships of A, C and D ;
|
||||
C and D will be able to end only their own membership.
|
||||
"""
|
||||
if user.has_perm("club.change_membership"):
|
||||
return self.all()
|
||||
return self.filter(
|
||||
Exists(
|
||||
Membership.objects.filter(
|
||||
Q(
|
||||
role__gt=Greatest(
|
||||
OuterRef("role"), Value(settings.SITH_MAXIMUM_FREE_ROLE)
|
||||
)
|
||||
)
|
||||
| Q(pk=OuterRef("pk")),
|
||||
user=user,
|
||||
end_date=None,
|
||||
club=OuterRef("club"),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def update(self, **kwargs) -> int:
|
||||
"""Refresh the cache and edit group ownership.
|
||||
|
||||
@@ -319,16 +355,12 @@ class Membership(models.Model):
|
||||
User,
|
||||
verbose_name=_("user"),
|
||||
related_name="memberships",
|
||||
null=False,
|
||||
blank=False,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
club = models.ForeignKey(
|
||||
Club,
|
||||
verbose_name=_("club"),
|
||||
related_name="members",
|
||||
null=False,
|
||||
blank=False,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
start_date = models.DateField(_("start date"), default=timezone.now)
|
||||
|
@@ -1,13 +1,15 @@
|
||||
from bs4 import BeautifulSoup
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.core.cache import cache
|
||||
from django.db.models import Max
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import localdate, localtime, now
|
||||
from model_bakery import baker
|
||||
|
||||
from club.forms import ClubMemberForm
|
||||
from club.models import Membership
|
||||
from club.models import Club, Membership
|
||||
from club.tests.base import TestClub
|
||||
from core.baker_recipes import subscriber_user
|
||||
from core.models import AnonymousUser, User
|
||||
@@ -137,6 +139,38 @@ class TestMembershipQuerySet(TestClub):
|
||||
assert set(user.groups.all()).isdisjoint(club_groups)
|
||||
|
||||
|
||||
class TestMembershipEditableBy(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
Membership.objects.all().delete()
|
||||
cls.club_a, cls.club_b = baker.make(Club, _quantity=2)
|
||||
cls.memberships = [
|
||||
*baker.make(
|
||||
Membership, role=iter([7, 3, 3, 1]), club=cls.club_a, _quantity=4
|
||||
),
|
||||
*baker.make(
|
||||
Membership, role=iter([7, 3, 3, 1]), club=cls.club_b, _quantity=4
|
||||
),
|
||||
]
|
||||
|
||||
def test_admin_user(self):
|
||||
perm = Permission.objects.get(codename="change_membership")
|
||||
user = baker.make(User, user_permissions=[perm])
|
||||
qs = Membership.objects.editable_by(user).values_list("id", flat=True)
|
||||
assert set(qs) == set(Membership.objects.values_list("id", flat=True))
|
||||
|
||||
def test_simple_subscriber_user(self):
|
||||
user = subscriber_user.make()
|
||||
assert not Membership.objects.editable_by(user).exists()
|
||||
|
||||
def test_board_member(self):
|
||||
# a board member can end lower memberships and its own one
|
||||
user = self.memberships[2].user
|
||||
qs = Membership.objects.editable_by(user).values_list("id", flat=True)
|
||||
expected = {self.memberships[2].id, self.memberships[3].id}
|
||||
assert set(qs) == expected
|
||||
|
||||
|
||||
class TestMembership(TestClub):
|
||||
def assert_membership_started_today(self, user: User, role: int):
|
||||
"""Assert that the given membership is active and started today."""
|
||||
|
Reference in New Issue
Block a user