Je voudrais utiliser un système basé sur des autorisations pour restreindre certaines actions dans mon Django. Ces actions n'ont pas besoin d'être liées à un modèle particulier (par exemple, l'accès aux sections de l'application, la recherche .. .), donc je ne peux pas utiliser le stock permissions framework directement, car le modèle Permission
nécessite une référence à un type de contenu installé.
Je pourrais écrire mon propre modèle d'autorisation, mais je devrais alors réécrire tous les goodies inclus avec les autorisations Django, telles que:
permission_required
décorateur .User.has_perm
et les méthodes utilisateur associées.perms
.J'ai vérifié certaines applications comme Django-Authority et Django-guardian , mais elles semblent fournir des autorisations encore plus couplées au système de modèles, en autorisant des autorisations par objet.
Existe-t-il un moyen de réutiliser ce framework sans avoir défini de modèle (à part User
et Group
) pour le projet?
Le modèle Permission
de Django nécessite une instance ContentType
.
Je pense que dans un sens, créer un ContentType
factice qui n'est lié à aucun modèle (le app_label
et model
peuvent être définis sur n'importe quelle valeur de chaîne).
Si vous voulez que tout soit propre et agréable, vous pouvez créer un Permission
modèle proxy qui gère tous les moches détails du mannequin ContentType
et crée des instances d'autorisation "sans modèle" . Vous pouvez également ajouter un gestionnaire personnalisé qui filtre toutes les instances Permission
liées aux modèles réels.
Pour ceux d'entre vous qui recherchent toujours:
Vous pouvez créer un modèle auxiliaire sans table de base de données. Ce modèle peut apporter à votre projet toute autorisation dont vous avez besoin. Il n'est pas nécessaire de traiter ContentType ou de créer des objets d'autorisation explicites.
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.
permissions = (
('customer_rights', 'Global customer rights'),
('vendor_rights', 'Global vendor rights'),
('any_rights', 'Global any rights'),
)
Juste après manage.py migrate
vous pouvez utiliser ces autorisations comme les autres.
# 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_rigths %}
<p>You can do any customer stuff</p>
{% endif %}
Suivant les conseils de Gonzalo , j'ai utilisé un modèle proxy et un gestionnaire personnalisé pour gérer mes autorisations "sans modèle" avec un type de contenu factice.
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)
Correction de la réponse de Chewie dans Django 1.8, comme demandé dans quelques commentaires.
Il est dit dans les notes de version:
Le champ de nom de Django.contrib.contenttypes.models.ContentType a été supprimé par une migration et remplacé par une propriété. Cela signifie qu'il n'est plus possible d'interroger ou de filtrer un ContentType par ce champ.
C'est donc le "nom" en référence dans ContentType que l'utilisent pas dans GlobalPermissions.
Lorsque je le répare, j'obtiens ce qui suit:
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 classe GlobalPermissionManager est inchangée mais incluse pour être complète.
Ceci est une solution alternative. Demandez-vous d'abord: pourquoi ne pas créer un modèle fictif qui existe vraiment dans la base de données mais qui n'est jamais utilisé, sauf pour les autorisations de détention? Ce n'est pas agréable, mais je pense que c'est une solution valable et simple.
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 solution ci-dessus présente l'avantage que vous pouvez utiliser la variable Permissions.can_search_blue_flower
dans votre code source au lieu d'utiliser la chaîne littérale "my_app.can_search_blue_flower". Cela signifie moins de fautes de frappe et plus de saisie semi-automatique dans IDE.
Au lieu d'écrire et d'exécuter ce code qui insère des enregistrements dans la base de données, vous pouvez simplement insérer les enregistrements dans votre base de données (en modifiant évidemment les clés primaires et étrangères selon les besoins)
insert into Django_content_type(id,name,app_label,model) values (22,'app_permission','myapp','app_permission');
insert into auth_permission(id,name,content_type_id,codename) values (64,'Is Staff Member',22,'staff_member');
Et puis, dans votre administrateur d'application, vous auriez la possibilité d'affecter "Est membre du personnel" à vos utilisateurs ou groupes. Pour vérifier cette autorisation dans votre classe, vous écririez
from Django.contrib.auth.decorators import permission_required
from Django.utils.decorators import method_decorator
from Django.views.generic import TemplateView
class MyClass(TemplateView):
template_name = myapp/index.html'
@method_decorator(permission_required(['myapp.staff_member'],raise_exception=True))
def dispatch(self, *args, **kwargs):
return super(MyClass, self).dispatch(*args, **kwargs)