Me gustaría usar un sistema basado en permisos para restringir ciertas acciones dentro de mi aplicación Django. Estas acciones no necesitan estar relacionadas con un modelo en particular (por ejemplo, acceso a secciones en la aplicación, búsqueda ...), por lo que no puedo usar el marco de permisos de stock directamente, porque el Permission
modelo requiere una referencia a un tipo de contenido instalado.
Podría escribir mi propio modelo de permisos, pero luego tendría que volver a escribir todas las ventajas incluidas con los permisos de Django, como:
- La posibilidad de asignar permisos a usuarios y grupos.
- El
permission_required
decorador . User.has_perm
y métodos de usuario relacionados.- La
perms
variable de plantilla . - ...
Revisé algunas aplicaciones como django-Authority y django-guardian , pero parecen proporcionar permisos aún más acoplados al sistema modelo, al permitir permisos por objeto.
¿Hay alguna forma de reutilizar este marco sin haber definido ningún modelo (además de User
y Group
) para el proyecto?
fuente
Para aquellos de ustedes que todavía están buscando:
Puede crear un modelo auxiliar sin tabla de base de datos. Ese modelo puede aportar a su proyecto cualquier permiso que necesite. No es necesario tratar con ContentType o crear objetos de permiso de forma explícita.
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'), )
Inmediatamente después
manage.py makemigrations
ymanage.py migrate
puede usar estos permisos como cualquier otro.# 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 %}
fuente
default_permissions = ()
. Esto evitará que Django cree automáticamente los permisos predeterminados de agregar / cambiar / eliminar / ver para este modelo, que probablemente sean innecesarios si está utilizando este enfoque.Siguiendo el consejo de Gonzalo , usé un modelo de proxy y un administrador personalizado para manejar mis permisos "sin modelo" con un tipo de contenido ficticio.
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)
fuente
FieldError: Cannot resolve keyword 'name' into field. Choices are: app_label, id, logentry, model, permission
.Arreglo para la respuesta de Chewie en Django 1.8, que como se solicitó en algunos comentarios.
Dice en las notas de la versión:
Entonces, es el 'nombre' en referencia en ContentType que no usa en GlobalPermissions.
Cuando lo arreglo, obtengo lo siguiente:
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)
La clase GlobalPermissionManager no se modifica, pero se incluye para completar.
fuente
Esta es una solución alternativa. Primero pregúntese: ¿Por qué no crear un modelo ficticio que realmente exista en la base de datos pero que nunca se use, excepto para tener permisos? Eso no es agradable, pero creo que es una solución válida y sencilla.
from django.db import models class Permissions(models.Model): can_search_blue_flower = 'my_app.can_search_blue_flower' class Meta: permissions = [ ('can_search_blue_flower', 'Allowed to search for the blue flower'), ]
La solución anterior tiene la ventaja de que puede usar la variable
Permissions.can_search_blue_flower
en su código fuente en lugar de usar la cadena literal "my_app.can_search_blue_flower". Esto significa menos errores tipográficos y más autocompletado en IDE.fuente
managed=False
Usar no te deja usarPermissions.can_search_blue_flower
por alguna razón?Puede utilizar
proxy model
para esto con un tipo de contenido ficticio.from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType class CustomPermission(Permission): class Meta: proxy = True 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(CustomPermission, self).save(*args)
Ahora puede crear el permiso con solo
name
ycodename
del permiso delCustomPermission
modelo.CustomPermission.objects.create(name='Can do something', codename='can_do_something')
Y puede consultar y mostrar solo los permisos personalizados en sus plantillas de esta manera.
CustomPermission.objects.filter(content_type__model='custom permission')
fuente