Django Admin – Contenu utilisateur spécifique (admin)

Je commence à organiser un nouveau projet et disons que je vais avoir quelques modèles comme des produits et des catalogues .

Je vais autoriser mes clients (pas les visiteurs, uniquement des clients spécifiques) à se connecter sur le site d’administration de Django pour créer, modifier et supprimer leurs propres catalogues.

Disons que je crée un modèle appelé “Shop” , crée chaque boutique (nom, adresse, logo, informations de contact, etc.) et crée un utilisateur administrateur lié à cette boutique.

Maintenant, je veux que ce nouvel administrateur (qui n’est pas un administrateur du site, mais un administrateur de la boutique – probablement un groupe d’utilisateurs ) ne puisse voir et éditer que les catalogues liés à sa boutique .

Est-ce possible?

Dois-je faire cela dans l’Admin Django ou devrais-je créer une nouvelle application “boutique admin”?

Tout d’abord, l’avertissement: La philosophie de conception de l’administrateur Django est que tout utilisateur ayant access à admin ( is_staff==True ) est un utilisateur de confiance , par exemple un employé, d’où la désignation “staff” pour accéder même à l’administrateur. Bien que vous puissiez personnaliser l’administrateur pour restreindre les zones, il est risqué de permettre à toute personne ne faisant pas partie de votre organisation d’accéder à l’administrateur, et Django ne donne aucune garantie à ce sujet.

Maintenant, si vous voulez toujours continuer, vous pouvez limiter la plupart des choses sauf les magasins en ne leur atsortingbuant tout simplement pas ces privilèges. Vous devrez donner à tous les propriétaires de magasins le droit de modifier les modèles de boutique auxquels ils auront besoin d’accéder, mais tout le rest devrait être supprimé de leur liste de permissions.

Ensuite, pour chaque modèle qui doit être limité aux yeux du propriétaire, vous devez append un champ pour stocker le “propriétaire” ou un utilisateur autorisé à y accéder. Vous pouvez le faire avec la méthode save_model sur ModelAdmin , qui a access à l’object request:

 class MyModelAdmin(admin.ModelAdmin): def save_model(self, request, obj, form, change): obj.user = request.user super(MyModelAdmin, self).save_model(request, obj, form, change) 

Ensuite, vous devrez également limiter le jeu de requête de ModelAdmin aux seuls éléments propres à l’utilisateur actuel:

 class MyModelAdmin(admin.ModelAdmin): def get_queryset(self, request): qs = super(MyModelAdmin, self).get_queryset(request) if request.user.is_superuser: return qs return qs.filter(owner=request.user) 

Cependant, cela ne fera que limiter ce qui est répertorié, l’utilisateur pourra toujours jouer avec l’URL pour accéder à d’autres objects auxquels il n’a pas access. Vous devrez donc remplacer chacune des vues vulnérables de ModelAdmin pour redirect l’utilisateur. le propriétaire:

 from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse class MyModelAdmin(admin.ModelAdmin): def change_view(self, request, object_id, form_url='', extra_context=None): if not self.queryset(request).filter(id=object_id).exists(): return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist')) return super(MyModelAdmin, self).change_view(request, object_id, form_url, extra_context) def delete_view(self, request, object_id, extra_context=None): if not self.queryset(request).filter(id=object_id).exists(): return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist')) return super(MyModelAdmin, self).delete_view(request, object_id, extra_context) def history_view(self, request, object_id, extra_context=None): if not self.queryset(request).filter(id=object_id).exists(): return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist')) return super(MyModelAdmin, self).history_view(request, object_id, extra_context) 

MISE À JOUR 06/05/12

Merci @ christophe31 pour avoir souligné que, puisque le ModelAdmin de ModelAdmin de ModelAdmin est déjà limité par l’utilisateur, vous pouvez simplement utiliser self.queryset() dans les vues de modification, de suppression et d’historique. Cela résume bien le nom de classe du modèle, rendant le code moins fragile. J’ai également changé pour utiliser le filter et exists au lieu d’ try...except bloquer avec get . Cette méthode est plus rationnelle et permet également une requête plus simple.

Je ne fais que poster ceci ici car le commentaire principal n’est plus la réponse la plus à jour. J’utilise Django 1.9, je ne sais pas quand cela a été fait.

Par exemple, vous avez différents sites et différents utilisateurs associés à chaque site, le modèle ressemblera à ceci:

 class Venue(models.Model): user = models.ForeignKey(User) venue_name = models.CharField(max_length=255) area = models.CharField(max_length=255) 

Maintenant, le statut du personnel pour l’utilisateur doit être vrai s’il est autorisé à se connecter via le panneau d’administration de Django.

L’admin.py ressemble à quelque chose comme:

 class FilterUserAdmin(admin.ModelAdmin): def save_model(self, request, obj, form, change): if getattr(obj, 'user', None) is None: obj.user = request.user obj.save() def get_queryset(self, request): qs = super(FilterUserAdmin, self).queryset(request) if request.user.is_superuser: return qs return qs.filter(user=request.user) def has_change_permission(self, request, obj=None): if not obj: return True return obj.user == request.user or request.user.is_superuser @admin.register(Venue) class VenueAdmin(admin.ModelAdmin): pass 

nom de la fonction a changé de queryset à get_queryset.

EDIT: Je voulais prolonger ma réponse. Il existe un autre moyen de retourner des objects filtrés sans utiliser la fonction queryset. Je tiens à souligner que je ne sais pas si cette méthode est plus efficace ou moins efficace.

Une autre implémentation de la méthode get_queryset est la suivante:

 def get_queryset(self, request): if request.user.is_superuser: return Venue.objects.all() else: return Venue.objects.filter(user=request.user) 

De plus, nous pouvons également filtrer le contenu si les relations sont plus profondes.

 class VenueDetails(models.Model): venue = models.ForeignKey(Venue) details = models.TextField() 

Maintenant, si je veux filtrer ce modèle qui a Venue en tant que clé étrangère sans utilisateur, ma requête change comme suit:

 def get_queryset(self, request): if request.user.is_superuser: return VenueDetails.objects.all() else: return VenueDetails.objects.filter(venue__user=request.user) 

Django ORM nous permet d’accéder à différents types de relations qui peuvent être aussi profondes que nous le souhaitons via ‘__’

Voici un lien vers les documents officiels ci-dessus.

Je suis désolé, je sais que c’est en retard, mais peut-être que cela aiderait quelqu’un d’autre. Je suppose que l’application django-permission pourrait aider à atteindre l’objective.

Je pense que RelatedOnlyFieldListFilter devrait vous aider. Voici le lien vers Django Doc: RelatedOnlyFieldListFilter

list_filter peut être: un tuple, où le premier élément est un nom de champ et le second élément est une classe héritant de django.consortingb.admin.FieldListFilter, par exemple:

 class PersonAdmin(admin.ModelAdmin): list_filter = ( ('is_staff', admin.BooleanFieldListFilter), ) 

Vous pouvez limiter les choix d’un modèle associé aux objects impliqués dans cette relation à l’aide de RelatedOnlyFieldListFilter: (Vous pouvez limiter les choix d’un modèle lié à des objects par la relation en utilisant RelatedOnlyFieldListFilter 🙂

  class BookAdmin(admin.ModelAdmin): list_filter = ( ('author', admin.RelatedOnlyFieldListFilter), ) 

En supposant que author est un ForeignKey vers un modèle User, cela limitera les choix list_filter aux utilisateurs qui ont écrit un livre au lieu de lister tous les utilisateurs . (En supposant que l’auteur est une clé ForeignKey dans un modèle d’utilisateur, cela va limiter les choix de list_filter aux utilisateurs qui ont écrit un livre à lieu pour tous les utilisateurs.)