membersystem/src/membership/models.py

92 lines
2.6 KiB
Python

from typing import Optional
from django.contrib.postgres.constraints import ExclusionConstraint
from django.contrib.postgres.fields import DateRangeField
from django.contrib.postgres.fields import RangeOperators
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext as _
from utils.mixins import CreatedModifiedAbstract
class SubscriptionPeriod(CreatedModifiedAbstract):
"""
Denotes a period for which members should pay their membership fee for.
"""
period = DateRangeField(verbose_name=_("period"))
class Meta:
constraints = [
ExclusionConstraint(
name="exclude_overlapping_periods",
expressions=[
("period", RangeOperators.OVERLAPS),
],
),
]
class Membership(CreatedModifiedAbstract):
"""
Tracks that a user has membership of a given type for a given period.
"""
class QuerySet(models.QuerySet):
def for_user(self, user):
return self.filter(user=user)
def _current(self):
return self.filter(period__period__contains=timezone.now())
def current(self) -> Optional["Membership"]:
try:
return self._current().get()
except self.model.DoesNotExist:
return None
def previous(self):
# A naïve way to get previous by just excluding the current. This
# means that there must be some protection against "future"
# memberships.
return self.all().difference(self._current())
objects = QuerySet.as_manager()
class Meta:
verbose_name = _("membership")
verbose_name_plural = _("memberships")
user = models.ForeignKey("auth.User", on_delete=models.PROTECT)
membership_type = models.ForeignKey(
"membership.MembershipType",
related_name="memberships",
verbose_name=_("subscription type"),
on_delete=models.PROTECT,
)
period = models.ForeignKey(
"membership.SubscriptionPeriod", on_delete=models.PROTECT
)
def __str__(self):
return f"{self.user} - {self.period}"
class MembershipType(CreatedModifiedAbstract):
"""
Models membership types. Currently only a name, but will in the future
possibly contain more information like fees.
"""
class Meta:
verbose_name = _("membership type")
verbose_name_plural = _("membership types")
name = models.CharField(verbose_name=_("name"), max_length=64)
def __str__(self):
return self.name