src/groups/models.py
from django.contrib.auth.models import Group, Permissionfrom django.db import modelsfrom django.utils.translation import gettext_lazy as _from django_hosts import reversefrom simple_history.models import HistoricalRecords from util.modelfields import CompressedImageFieldfrom util.storage import OverwriteStorage, UploadToUtils class InheritanceGroup(Group): """ A group that allow inheritance of permissions. The groups that a group will inherit from, are given by the ``parents`` field. The permissions that this group has independently of its parents, are given by the ``own_permissions`` field. The standard ``permissions`` field will contain the group's own permissions, and those it has inherited. This field should not be altered, as any change will get overwritten. """ parents = models.ManyToManyField( to='self', symmetrical=False, blank=True, related_name='children', verbose_name=_("parents"), ) own_permissions = models.ManyToManyField( to=Permission, blank=True, related_name='inheritance_groups', verbose_name=_("own permissions"), ) last_modified = models.DateTimeField(auto_now=True, verbose_name=_("last modified")) TODO found # TODO: Add `parents` to `m2m_fields` when https://github.com/jazzband/django-simple-history/issues/1126 is resolved history = HistoricalRecords(m2m_fields=[own_permissions], excluded_fields=['last_modified']) @property def inherited_permissions(self): return set(self.permissions.all()) - set(self.own_permissions.all()) def update_permissions(self): """Update the permissions of this group and all its children.""" own_permissions = list(self.own_permissions.all()) for parent in self.parents.all(): own_permissions.extend(parent.permissions.all()) self.permissions.set(own_permissions) for child in self.children.all(): child.update_permissions() Similar blocks of code found in 2 locations. Consider refactoring. def get_all_children(self): """Return a queryset of all groups that inherit from this group.""" children = self.children.all() for child in self.children.all(): children = children.union(child.get_all_children()) return children Similar blocks of code found in 2 locations. Consider refactoring. def get_all_parents(self): """Return a queryset of all groups that this group inherits from.""" parents = self.parents.all() for parent in self.parents.all(): parents = parents.union(parent.get_all_parents()) return parents def get_available_parents(self): """ Return a queryset of all groups that can be a parent to this group. This excludes any group that inherits from this group, as that would cause a circular dependency. """ parents = InheritanceGroup.objects.exclude(pk=self.pk) for child in self.get_all_children(): parents = parents.exclude(pk=child.pk) return parents class Committee(models.Model): """ A committee in the organization. A committee gets its name and members through the :model:`groups.InheritanceGroup` given in the ``group`` field. """ group = models.OneToOneField( to=InheritanceGroup, on_delete=models.CASCADE, related_name='committee', verbose_name=_("group"), ) clickbait = models.TextField(blank=True, verbose_name=_("clickbait")) description = models.TextField(verbose_name=_("description")) email = models.EmailField(verbose_name=_("email")) image = CompressedImageField(upload_to=UploadToUtils.get_pk_prefixed_filename_func('committees'), blank=True, max_length=200, storage=OverwriteStorage(), verbose_name=_("image")) last_modified = models.DateTimeField(auto_now=True, verbose_name=_("last modified")) history = HistoricalRecords(excluded_fields=['last_modified']) def __str__(self): return self.name def get_absolute_url(self): return reverse('committee_detail', args=[self.pk]) @property def name(self): return self.group.name