How to add custom permission to the User model in django?
Asked Answered
S

5

22

in django by default when syncdb is run with django.contrib.auth installed, it creates default permissions on each model... like foo.can_change , foo.can_delete and foo.can_add. To add custom permissions to models one can add class Meta: under the model and define permissions there, as explained here https://docs.djangoproject.com/en/4.1/topics/auth/customizing/#custom-permissions

My question is that what should I do if I want to add a custom permission to the User model? like foo.can_view. I could do this with the following snippet,

ct = ContentType.objects.get(app_label='auth', model='user')
perm = Permission.objects.create(codename='can_view', name='Can View Users', 
                                  content_type=ct)
perm.save()

But I want something that plays nicely with syncdb, for example the class Meta under my custom models. Should I just have these in class Meta: under UserProfile since that is the way to extend the user model. but is that the RIGHT way to do it? Wouldn't that tie it to UserProfile model?

Shipmaster answered 11/10, 2011 at 9:57 Comment(1)
You can do that in model migration: https://mcmap.net/q/589358/-django-data-migrate-permissionsRoasting
E
9

You could do something like this:

in the __init__.py of your Django app add:

from django.db.models.signals import post_syncdb
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth import models as auth_models
from django.contrib.auth.models import Permission

# custom user related permissions
def add_user_permissions(sender, **kwargs):
    ct = ContentType.objects.get(app_label='auth', model='user')
    perm, created = Permission.objects.get_or_create(codename='can_view', name='Can View Users', content_type=ct)
post_syncdb.connect(add_user_permissions, sender=auth_models)
Excessive answered 13/11, 2012 at 12:30 Comment(3)
Putting in an __init__.py and connecting to post_syncdb is probably a good idea, but the code above didn't work for me. I have to use ContentType.objects.get_for_model(model=auth_models.User). Otherwise, it worked.Beekeeping
post_syncdb doesn't seem to be a signal anymorePleopod
Use the defaults dict to pass name to get_or_create, like this: defaults={'name': 'Can View Users'}. More infoMultiplier
V
5

I don't think there is a "right" answer here, but i used the exact same code as you except i changed Permission.objects.create to Permission.objects.get_or_create and that worked find to sync with syncdb

Volition answered 9/3, 2012 at 20:17 Comment(2)
won't get_or_create, create another permission if name changes but codename remains the same ?Variolite
you should use the codename only as the get part of get or create, and put the name in "defaults"Volition
S
2

An updated answer for Django 1.8. The signal pre_migrate is used instead of pre_syncdb, since syncdb is deprecated and the docs recommend using pre_migrate instead of post_migrate if the signal will alter the database. Also, @receiver is used to connect add_user_permissions to the signal.

from django.db.models.signals import pre_migrate
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth import models as auth_models
from django.contrib.auth.models import Permission
from django.conf import settings
from django.dispatch import receiver


# custom user related permissions
@receiver(pre_migrate, sender=auth_models)
def add_user_permissions(sender, **kwargs):
    content_type = ContentType.objects.get_for_model(settings.AUTH_USER_MODEL)
    Permission.objects.get_or_create(codename='view_user', name='View user', content_type=content_type)
Sectionalize answered 1/8, 2015 at 14:42 Comment(3)
May i ask in which file do i have to put this code? I though it was to be set in models as in earlier version of Django but I'm not sure...Linder
@Sectionalize I put this in __init__.py but the "DoesNotExist: ContentType matching query does not exist." error is still comingOmar
I'm not sure your sender argument is correct, it should be an app instance, e.g. ``` from django.apps import apps sender = apps.get_app_config('auth') ```Ultramicrometer
H
1

This is a bit hacky but mentioning it here anyway for reference.

My site has a generic model called Setting, which stores various settings concerning the site I want certain users to be able to edit, without needing to go through me the developer (like registration limit, or an address, or the cost of items, etc).

All the permissions that don't nicely map onto other models (eg "Send Password Reminder Email to Student", "Generate Payment Reconciliation Report", "Generate PDF Receipt"), which really just relate to pages that get viewed in the admin area, get dumped onto this Setting model.

For example, here's the model:

class Setting(models.Model):
    name = models.CharField(max_length=50, unique=True)
    slug = models.SlugField(editable=False)
    description = models.TextField()
    value = models.TextField()
    class Meta:
        #for permissions that don't really relate to a particular model, and I don't want to programmatically create them.
        permissions = (
            ("password_reminder", "Send Password Reminder"),
            ("generate_payment_reconciliation_report", "Generate Payment Reconciliation Report"),
            ("generate_pdf_receipt", "Generate PDF Receipt"),
        )

Do each of those settings strictly relate to the Setting model? No, which is why I said this is a bit hacky. But it is nice that I can now just dump all those permissions here, and Django's migration commands will take care of the rest.

Hose answered 14/6, 2019 at 18:22 Comment(0)
M
0

As @leech pointed out in a comment on their answer, you can't pass the name parameter to get_or_create directly. You have to include name as part of the defaults dict. Otherwise you'll get duplicate key errors.

Change the get_or_create call from @Dzejkob's answer to make it look like this:

perm, created = Permission.objects.get_or_create(
    content_type=ct,
    codename='can_view', 
    defaults={'name': 'Can View Users'},
)
Multiplier answered 12/5, 2023 at 21:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.