I've been wrestling with this for a couple of days now. django.contrib.auth.models has a User model that allows multiple permissions, but then has 3 flags for is_staff, is_superuser, and is_active.
is_staff = models.BooleanField(_('staff status'), default=False, help_text=_('Designates whether the user can log into this admin site.'))
is_active = models.BooleanField(_('active'), default=True, help_text=_('Designates whether this user should be treated as active. Unselect this instead of deleting accounts.'))
is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_('Designates that this user has all permissions without explicitly assigning them.'))
groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True, help_text=_('The groups this user belongs to. A user will get all permissions granted to each of his/her group.'))
user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True, help_text='Specific permissions for this user.')
What I can't understand is why is_staff and is_superuser aren't permissions, you could implement it that way with about the same amount of code, but for some reason they chose to move those two fields to booleans on the model.
Part of the reason I'm asking is because I have a similar situation that could use a third boolean (or I could do it with a permission) and I'm trying to understand why they would separate those two from the permissions?
I think I understand is_active, that allows you to lock someone out without having to "fix" their permissions when they return (although you could have an is_locked_out "permission", but that just sounds wrong.)
I'm going to guess performance. Checking a boolean field is much cheaper then having to grab permissions related to the user from another table or do sql joins. Considering that in some cases, the users authorisation status is checked on every request, it's much more sensible (albeit not as clean) to put booleans on the user model and check those.