How can I use Django permissions without defining a content type or model?
For those of you, who are still searching:
You can create an auxiliary model with no database table. That model can bring to your project any permission you need. There is no need to deal with ContentType or create Permission objects explicitly.
from django.db import models
class RightsSupport(models.Model):
class Meta:
managed = False # No database table creation or deletion \
# operations will be performed for this model.
default_permissions = () # disable "add", "change", "delete"
# and "view" default permissions
permissions = (
('customer_rights', 'Global customer rights'),
('vendor_rights', 'Global vendor rights'),
('any_rights', 'Global any rights'),
)
Right after manage.py makemigrations
and manage.py migrate
you can use these permissions like any other.
# Decorator
@permission_required('app.customer_rights')
def my_search_view(request):
…
# Inside a view
def my_search_view(request):
request.user.has_perm('app.customer_rights')
# In a template
# The currently logged-in user’s permissions are stored in the template variable {{ perms }}
{% if perms.app.customer_rights %}
<p>You can do any customer stuff</p>
{% endif %}
Fix for Chewie's answer in Django 1.8, which as been requested in a few comments.
It says in the release notes:
The name field of django.contrib.contenttypes.models.ContentType has been removed by a migration and replaced by a property. That means it’s not possible to query or filter a ContentType by this field any longer.
So it's the 'name' in reference in ContentType that the uses not in GlobalPermissions.
When I fix it I get the following:
from django.db import models
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
class GlobalPermissionManager(models.Manager):
def get_queryset(self):
return super(GlobalPermissionManager, self).\
get_queryset().filter(content_type__model='global_permission')
class GlobalPermission(Permission):
"""A global permission, not attached to a model"""
objects = GlobalPermissionManager()
class Meta:
proxy = True
verbose_name = "global_permission"
def save(self, *args, **kwargs):
ct, created = ContentType.objects.get_or_create(
model=self._meta.verbose_name, app_label=self._meta.app_label,
)
self.content_type = ct
super(GlobalPermission, self).save(*args)
The GlobalPermissionManager class is unchanged but included for completeness.
Django's Permission
model requires a ContentType
instance.
I think one way around it is creating a dummy ContentType
that isn't related to any model (the app_label
and model
fields can be set to any string value).
If you want it all clean and nice, you can create a Permission
proxy model that handles all the ugly details of the dummy ContentType
and creates "modelless" permission instances. You can also add a custom manager that filters out all Permission
instances related to real models.
Following Gonzalo's advice, I used a proxy model and a custom manager to handle my "modelless" permissions with a dummy content type.
from django.db import models
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
class GlobalPermissionManager(models.Manager):
def get_query_set(self):
return super(GlobalPermissionManager, self).\
get_query_set().filter(content_type__name='global_permission')
class GlobalPermission(Permission):
"""A global permission, not attached to a model"""
objects = GlobalPermissionManager()
class Meta:
proxy = True
def save(self, *args, **kwargs):
ct, created = ContentType.objects.get_or_create(
name="global_permission", app_label=self._meta.app_label
)
self.content_type = ct
super(GlobalPermission, self).save(*args, **kwargs)