diff --git a/biscuit/core/mixins.py b/biscuit/core/mixins.py index 684e9f401e146eb8783f824454ebe6aeaaac4fc3..8b31557cf9b23ca15a4bc6ce5d3979efc4f954fa 100644 --- a/biscuit/core/mixins.py +++ b/biscuit/core/mixins.py @@ -1,3 +1,5 @@ +from typing import Any, Callable, Optional + from django.contrib.contenttypes.models import ContentType from django.db import models from django.db.models import QuerySet @@ -7,6 +9,30 @@ from easyaudit.models import CRUDEvent from .util.core_helpers import get_current_school +class ExtensibleModel(object): + """ Mixin that adds class methods for glrofied monkey-patching. """ + + @classmethod + def property(cls, func: Callable[[], Any], name: Optional[str] = None) -> None: + """ Adds the passed callable as a property. """ + + # Decide the name for the property + if name is None: + prop_name = func.__name__ + else: + if name.isidentifier(): + prop_name = name + else: + raise ValueError('%s is not a valid name.' % name) + + # Verify that property name does not clash with other names in the class + if hasattr(cls, prop_name): + raise ValueError('%s already used.' % prop_name) + + # Add function wrapped in property decorator if we got here + setattr(cls, prop_name, property(func)) + + class SchoolRelated(models.Model): class Meta: abstract = True diff --git a/biscuit/core/models.py b/biscuit/core/models.py index d726602eb2b72bab7d49543b073b556afa5d8607..63e4bde6e4d99c5f09c2a2ff75afacab50bc0f83 100644 --- a/biscuit/core/models.py +++ b/biscuit/core/models.py @@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _ from image_cropping import ImageCropField, ImageRatioField from phonenumber_field.modelfields import PhoneNumberField -from .mixins import SchoolRelated +from .mixins import ExtensibleModel, SchoolRelated class School(models.Model): @@ -42,9 +42,9 @@ class SchoolTerm(SchoolRelated): 'Effective start date of term'), null=True) date_end = models.DateField(verbose_name=_( 'Effective end date of term'), null=True) - -class Person(SchoolRelated): + +class Person(SchoolRelated, ExtensibleModel): """ A model describing any person related to a school, including, but not limited to, students, teachers and guardians (parents). """ @@ -132,7 +132,7 @@ class Person(SchoolRelated): return self.full_name -class Group(SchoolRelated): +class Group(SchoolRelated, ExtensibleModel): """Any kind of group of persons in a school, including, but not limited classes, clubs, and the like. """