Assigning Object Permissions
Django Guardian makes assigning object permissions simple once permissions are created for models.
Prepare permissions
Take the below example model:
class Task(models.Model):
summary = models.CharField(max_length=32)
content = models.TextField()
reported_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
... and we want to be able to set custom permission assign_task.
We can let Django know about our custom permissions by adding a permissions
tuple to Meta class and our final model could look like:
class Task(models.Model):
summary = models.CharField(max_length=32)
content = models.TextField()
reported_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
permissions = (
('assign_task', 'Assign task'),
)
After we call management commands makemigrations and migrate our
assign_task permission would be added to default set of permissions.
Note
By default, Django adds 4 permissions for each registered model:
- add_modelname
- change_modelname
- delete_modelname
- view_modelname
(where modelname is a simplified name of our model's class). See https://docs.djangoproject.com/en/stable/topics/auth/default/#default-permissions for more detail.
There is nothing new here since creation of permissions is handled by
django. Now we
can move to assigning object permissions <assign-obj-perms>
Assign object permissions
We can assign permissions for any user or group and object pairs
using the convenient function: guardian.shortcuts.assign_perm()
For user
Continuing our example we now can allow Joe user to assign some task:
>>> from django.contrib.auth.models import User
>>> boss = User.objects.create(username='Big Boss')
>>> joe = User.objects.create(username='joe')
>>> task = Task.objects.create(summary='Some job', content='', reported_by=boss)
>>> joe.has_perm('assign_task', task)
False
Well, not so fast Joe, let us create an object permission finally:
>>> from guardian.shortcuts import assign_perm
>>> assign_perm('assign_task', joe, task)
>>> joe.has_perm('assign_task', task)
True
For group
This case doesn't really differ from user permissions assignment.
The only difference is we have to pass Group instance rather than User.
>>> from django.contrib.auth.models import Group
>>> group = Group.objects.create(name='employees')
>>> assign_perm('change_task', group, task)
>>> joe.has_perm('change_task', task)
False
>>> # Well, joe is not yet within an *employees* group
>>> joe.groups.add(group)
>>> joe.has_perm('change_task', task)
True
Another example:
>>> from django.contrib.auth.models import User, Group
>>> from guardian.shortcuts import assign_perm
# fictional companies
>>> company_a = Company.objects.create(name="Company A")
>>> company_b = Company.objects.create(name="Company B")
# create groups
>>> company_user_group_a = Group.objects.create(name="Company User Group A")
>>> company_user_group_b = Group.objects.create(name="Company User Group B")
# assign object specific permissions to groups
>>> assign_perm('change_company', company_user_group_a, company_a)
>>> assign_perm('change_company', company_user_group_b, company_b)
# create user and add it to one group for testing
>>> user_a = User.objects.create(username="User A")
>>> user_a.groups.add(company_user_group_a)
>>> user_a.has_perm('change_company', company_a)
True
>>> user_a.has_perm('change_company', company_b)
False
>>> user_b = User.objects.create(username="User B")
>>> user_b.groups.add(company_user_group_b)
>>> user_b.has_perm('change_company', company_a)
False
>>> user_b.has_perm('change_company', company_b)
True
Assigning Permissions inside Signals
Note that the Anonymous User is created before the Permissions are
created. This may result in Django signals, e.g. post_save being sent
before the Permissions are created. You will need to take this into an
account when processing the signal.
@receiver(post_save, sender=User)
def user_post_save(sender, **kwargs):
"""
Create a Profile instance for all newly created User instances. We only
run on user creation to avoid having to check for existence on each call
to User.save.
"""
user, created = kwargs["instance"], kwargs["created"]
if created and user.username != settings.ANONYMOUS_USER_NAME:
from profiles.models import Profile
profile = Profile.objects.create(pk=user.pk, user=user, creator=user)
assign_perm("change_user", user, user)
assign_perm("change_profile", user, profile)
The check for user.username != settings.ANONYMOUS_USER_NAME is
required otherwise the assign_perm calls will occur when the Anonymous
User is created, however before there are any permissions available.