Warm tip: This article is reproduced from stackoverflow.com, please click
django django-models hierarchical-data python python-3.x

Basic group hierarchy in python django

发布于 2020-03-27 10:21:33

I'm building a student registration system by using django where students are registered. Students can be viewed only by their class teachers and school principals based on object level permission. There are School, Class and Student models. Each school can have more than one school principals and each class can have more than one class teachers.

There will be two object level permissions:

  1. School principals will be able to see all of the students registered to their school. They won't be able to see students of other schools.
  2. Class teachers will be able to see the students that are registered to their classes. They won't be able to see students registered to other classes in the same or different schools.

I have searched through various 3rd party django libraries to implement such a hierarchical user group architecture. I have seen django-groups-manager, but it is a bit complicated for my issue. Then, I decided on django-mptt's registration of existing models feature and come up with model implementations such as:

from django.contrib.auth.models import Group
from django.db import models

import mptt
from mptt.fields import TreeForeignKey

TreeForeignKey(Group, on_delete=models.CASCADE, blank=True,
               null=True).contribute_to_class(Group, 'parent')
mptt.register(Group, order_insertion_by=['name'])


class School(models.Model):
    """School object"""

    name = models.CharField(max_length=255)
    group = models.ForeignKey(
        Group, related_name='school', on_delete=models.CASCADE)


class Class(models.Model):
    """Class object"""

    name = models.CharField(max_length=255)
    group = models.ForeignKey(
        Group, related_name='class', on_delete=models.CASCADE)
    school = models.ForeignKey(
        School,
        on_delete=models.CASCADE
    )


class Student(models.Model):
    """Person object"""

    fullname = models.CharField(max_length=255)
    class = models.ForeignKey(
        Class,
        on_delete=models.CASCADE
    )

In this way, each School and Class objects will have their own Groups, school group being parent of the classes' groups of that particular school. So I can now create school principal users by using django's User and assign it to the related parent group. By the same way, I can also create teacher users and assign them to their children groups of their class objects. And when a school principal or class teachers want to view their registered students, I can apply object level permission by filtering to their user groups.

My question is, is it the right way to do it? Is creating one group per each school/class objects meaningful?

Questioner
cagrias
Viewed
114
cagrias 2019-07-03 22:13

Thanks to nigel222's suggestion, instead of using built-in django groups, I have overriden built-in django User model. I have also followed this tutorial which was really helpful. My latest version of models.py is as follows (Don't forget to override default User class, you need to let django know about it by adding AUTH_USER_MODEL = 'myapp.User' to your settings.py file):

from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):
    """Custom user model with an extra type field"""
    USER_TYPE_CHOICES = (
        (1, 'superuser'),
        (2, 'principaluser'),
        (3, 'teacheruser'),
    )

    user_type = models.PositiveSmallIntegerField(choices=USER_TYPE_CHOICES)


class School(models.Model):
    """School object"""

    name = models.CharField(max_length=255)
    users = models.ManyToManyField(settings.AUTH_USER_MODEL)


class Class(models.Model):
    """Class object"""

    name = models.CharField(max_length=255)
    users = models.ManyToManyField(settings.AUTH_USER_MODEL)
    school = models.ForeignKey(
        School,
        on_delete=models.CASCADE
    )


class Student(models.Model):
    """Person object"""

    fullname = models.CharField(max_length=255)
    class = models.ForeignKey(
        Class,
        on_delete=models.CASCADE
    )