web-dev-qa-db-fra.com

Django Admin - Contenu utilisateur (admin) spécifique

Je commence à organiser un nouveau projet et disons que j'aurai quelques modèles comme produits et catalogues.

Je vais autoriser mes clients (pas les visiteurs, seulement des clients spécifiques) à se connecter sur Django Site Admin pour créer, éditer et supprimer leurs propres catalogues.

Disons que je crée un modèle appelé "Boutique", crée chaque boutique (nom, adresse, logo, coordonnées, etc.) et crée un utilisateur administrateur lié à cette boutique.

Maintenant, je veux que ce nouvel administrateur (qui n'est pas un administrateur de site, mais un administrateur de boutique - probablement un groupe d'utilisateurs) puisse voir et modifier niquement les catalogues liés à sa boutique.

Est-ce possible?

Dois-je le faire à l'intérieur de l'administration Django ou dois-je créer une nouvelle application "administrateur de boutique"?

48
Thiago Belem

Tout d'abord, l'avertissement de mise en garde: la philosophie de conception de l'administration Django admin est que tout utilisateur ayant accès à l'administrateur (is_staff==True) Est un de confiance utilisateur, par exemple un employé, d'où la désignation "personnel" pour même accéder à l'administrateur. Bien que vous puissiez personnaliser l'administrateur pour restreindre les zones, autoriser toute personne ne faisant pas partie de votre organisation à accéder à votre administrateur est considérée risqué, et Django ne fait aucune garantie sur aucune sorte de sécurité à ce stade.

Maintenant, si vous voulez continuer, vous pouvez restreindre presque tout sauf les magasins dès le départ en n'attribuant tout simplement pas ces privilèges à l'utilisateur. Vous devrez donner à tous les propriétaires de magasins le droit de modifier l'un des modèles de magasins auxquels ils auront besoin d'accéder, mais tout le reste doit être supprimé de leur liste d'autorisations.

Ensuite, pour chaque modèle qui doit être limité aux seuls yeux du propriétaire, vous devrez ajouter un champ pour stocker le "propriétaire" ou l'utilisateur autorisé à y accéder. Vous pouvez le faire avec la méthode save_model Sur ModelAdmin, qui a accès à l'objet de requête:

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êtes de ModelAdmin aux seuls éléments appartenant à 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 limitera que ce qui est répertorié, l'utilisateur peut toujours jouer avec l'URL pour accéder à d'autres objets auxquels il n'a pas accès, vous devrez donc remplacer chacune des vues vulnérables de ModelAdmin pour rediriger si l'utilisateur n'est pas 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 d'avoir souligné que puisque l'ensemble de requêtes 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, ce qui rend le code moins fragile. J'ai également changé pour utiliser filter et exists au lieu d'un bloc try...except Avec get. Il est plus rationalisé de cette façon et se traduit également par une requête plus simple.

59
Chris Pratt

Je ne fais que poster ceci ici car le premier commentaire n'est plus la réponse la plus à jour. J'utilise Django 1.9, je ne sais pas quand ce changement a eu lieu.

Par exemple, vous avez différents lieux et différents utilisateurs associés à chaque lieu, 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)

Désormais, l'état du personnel de l'utilisateur doit être vrai s'il est autorisé à se connecter via le panneau d'administration Django.

Le fichier admin.py ressemble à ceci:

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

le nom de la fonction est passé de queryset à get_queryset.

EDIT: Je voulais étendre ma réponse. Il existe une autre façon de renvoyer des objets 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 implémentation alternative pour 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 comme clé étrangère mais qui n'a pas d'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 pour ce qui précède.

12
chatuur

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 deuxième élément est une classe héritant de Django.contrib.admin.FieldListFilter, par exemple:

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

Vous pouvez limiter les choix d'un modèle lié aux objets impliqués dans cette relation en utilisant RelatedOnlyFieldListFilter: (Vous pouvez limiter le choix d'un modèle lié aux objets concernés par la relation en utilisant RelatedOnlyFieldListFilter :)

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

En supposant que l'auteur est une clé étrangère vers un modèle utilisateur, cela limitera les choix list_filter aux utilisateurs qui ont écrit un livre au lieu de répertorier tous les utilisateurs . (En supposant que author est une clé ForeignKey vers un modèle User, cela va limiter les choix de liste_filtre aux utilisateurs qui ont écrit un livre au lieu d’énumérer tous les utilisateurs.)

0
HamzDiou

Je suis désolé, je sais qu'il est tard, mais cela aiderait peut-être quelqu'un d'autre. Je suppose que l'application Django-permission pourrait aider à atteindre cet objectif.

0
Khchine Hamza