Aller au contenu

Models

Club

Bases: Model

The Club class, made as a tree to allow nice tidy organization.

president()

Fetch the membership of the current president of this club.

Source code in club/models.py
@cached_property
def president(self) -> Membership | None:
    """Fetch the membership of the current president of this club."""
    return self.members.filter(
        role=settings.SITH_CLUB_ROLES_ID["President"], end_date=None
    ).first()

check_loop()

Raise a validation error when a loop is found within the parent list.

Source code in club/models.py
def check_loop(self):
    """Raise a validation error when a loop is found within the parent list."""
    objs = []
    cur = self
    while cur.parent is not None:
        if cur in objs:
            raise ValidationError(_("You can not make loops in clubs"))
        objs.append(cur)
        cur = cur.parent

is_owned_by(user)

Method to see if that object can be super edited by the given user.

Source code in club/models.py
def is_owned_by(self, user: User) -> bool:
    """Method to see if that object can be super edited by the given user."""
    if user.is_anonymous:
        return False
    return user.is_root or user.is_board_member

can_be_edited_by(user)

Method to see if that object can be edited by the given user.

Source code in club/models.py
def can_be_edited_by(self, user: User) -> bool:
    """Method to see if that object can be edited by the given user."""
    return self.has_rights_in_club(user)

can_be_viewed_by(user)

Method to see if that object can be seen by the given user.

Source code in club/models.py
def can_be_viewed_by(self, user: User) -> bool:
    """Method to see if that object can be seen by the given user."""
    return user.was_subscribed

get_membership_for(user)

Return the current membership the given user.

Note

The result is cached.

Source code in club/models.py
def get_membership_for(self, user: User) -> Membership | None:
    """Return the current membership the given user.

    Note:
        The result is cached.
    """
    if user.is_anonymous:
        return None
    membership = cache.get(f"membership_{self.id}_{user.id}")
    if membership == "not_member":
        return None
    if membership is None:
        membership = self.members.filter(user=user, end_date=None).first()
        if membership is None:
            cache.set(f"membership_{self.id}_{user.id}", "not_member")
        else:
            cache.set(f"membership_{self.id}_{user.id}", membership)
    return membership

MembershipQuerySet

Bases: QuerySet

ongoing()

Filter all memberships which are not finished yet.

Source code in club/models.py
def ongoing(self) -> Self:
    """Filter all memberships which are not finished yet."""
    return self.filter(Q(end_date=None) | Q(end_date__gt=localdate()))

board()

Filter all memberships where the user is/was in the board.

Be aware that users who were in the board in the past are included, even if there are no more members.

If you want to get the users who are currently in the board, mind combining this with the :meth:ongoing queryset method

Source code in club/models.py
def board(self) -> Self:
    """Filter all memberships where the user is/was in the board.

    Be aware that users who were in the board in the past
    are included, even if there are no more members.

    If you want to get the users who are currently in the board,
    mind combining this with the :meth:`ongoing` queryset method
    """
    return self.filter(role__gt=settings.SITH_MAXIMUM_FREE_ROLE)

update(**kwargs)

Refresh the cache and edit group ownership.

Update the cache, when necessary, remove users from club groups they are no more in and add them in the club groups they should be in.

Be aware that this adds three db queries : one to retrieve the updated memberships, one to perform group removal and one to perform group attribution.

Source code in club/models.py
def update(self, **kwargs) -> int:
    """Refresh the cache and edit group ownership.

    Update the cache, when necessary, remove
    users from club groups they are no more in
    and add them in the club groups they should be in.

    Be aware that this adds three db queries :
    one to retrieve the updated memberships,
    one to perform group removal and one to perform
    group attribution.
    """
    nb_rows = super().update(**kwargs)
    if nb_rows == 0:
        # if no row was affected, no need to refresh the cache
        return 0

    cache_memberships = {}
    memberships = set(self.select_related("club"))
    # delete all User-Group relations and recreate the necessary ones
    # It's more concise to write and more reliable
    Membership._remove_club_groups(memberships)
    Membership._add_club_groups(memberships)
    for member in memberships:
        cache_key = f"membership_{member.club_id}_{member.user_id}"
        if member.end_date is None:
            cache_memberships[cache_key] = member
        else:
            cache_memberships[cache_key] = "not_member"
    cache.set_many(cache_memberships)
    return nb_rows

delete()

Work just like the default Django's delete() method, but add a cache invalidation for the elements of the queryset before the deletion, and a removal of the user from the club groups.

Be aware that this adds some db queries :

  • 1 to retrieve the deleted elements in order to perform post-delete operations. As we can't know if a delete will affect rows or not, this query will always happen
  • 1 query to remove the users from the club groups. If the delete operation affected no row, this query won't happen.
Source code in club/models.py
def delete(self) -> tuple[int, dict[str, int]]:
    """Work just like the default Django's delete() method,
    but add a cache invalidation for the elements of the queryset
    before the deletion,
    and a removal of the user from the club groups.

    Be aware that this adds some db queries :

    - 1 to retrieve the deleted elements in order to perform
      post-delete operations.
      As we can't know if a delete will affect rows or not,
      this query will always happen
    - 1 query to remove the users from the club groups.
      If the delete operation affected no row,
      this query won't happen.
    """
    memberships = set(self.all())
    nb_rows, rows_counts = super().delete()
    if nb_rows > 0:
        Membership._remove_club_groups(memberships)
        cache.set_many(
            {
                f"membership_{m.club_id}_{m.user_id}": "not_member"
                for m in memberships
            }
        )
    return nb_rows, rows_counts

Membership

Bases: Model

The Membership class makes the connection between User and Clubs.

Both Users and Clubs can have many Membership objects
  • a user can be a member of many clubs at a time
  • a club can have many members at a time too

A User is currently member of all the Clubs where its Membership has an end_date set to null/None. Otherwise, it's a past membership kept because it can be very useful to see who was in which Club in the past.

is_owned_by(user)

Method to see if that object can be super edited by the given user.

Source code in club/models.py
def is_owned_by(self, user: User) -> bool:
    """Method to see if that object can be super edited by the given user."""
    if user.is_anonymous:
        return False
    return user.is_root or user.is_board_member

can_be_edited_by(user)

Check if that object can be edited by the given user.

Source code in club/models.py
def can_be_edited_by(self, user: User) -> bool:
    """Check if that object can be edited by the given user."""
    if user.is_root or user.is_board_member:
        return True
    membership = self.club.get_membership_for(user)
    return membership is not None and membership.role >= self.role

Mailing

Bases: Model

A Mailing list for a club.

Warning

Remember that mailing lists should be validated by UTBM.

MailingSubscription

Bases: Model

Link between user and mailing list.

get_default_owner_group()

Source code in club/models.py
def get_default_owner_group():
    return settings.SITH_GROUP_ROOT_ID