Désactiver le lien pour modifier l'objet dans l'admin de django (afficher la liste seulement)?

Dans Django admin, je veux désactiver les liens fournis sur la page" Sélectionner un élément à modifier " afin que les utilisateurs ne puissent aller nulle part pour modifier l'élément. (Je vais limiter ce que les utilisateurs peuvent faire avec cette liste à un ensemble d'actions déroulantes - pas d'édition réelle des champs).

je vois que Django a la capacité de choisissez les champs qui affichent le lien cependant, je ne vois pas comment je peux avoir aucun.

class HitAdmin(admin.ModelAdmin):
    list_display = ('user','ip','user_agent','hitcount')
    search_fields = ('ip','user_agent')
    date_hierarchy = 'created'
    list_display_links = [] # doesn't work, goes to default

tous idées comment obtenir ma liste d'objets sans aucun lien à éditer?

33
demandé sur thornomad 2009-10-24 22:42:22

9 réponses

je voulais un visualiseur de Log comme liste seulement.

je l'ai eu à travailler comme cela:

class LogEntryAdmin(ModelAdmin):
    actions = None
    list_display = (
        'action_time', 'user',
        'content_type', 'object_repr', 
        'change_message')

    search_fields = ['=user__username', ]
    fieldsets = [
        (None, {'fields':()}), 
        ]

    def __init__(self, *args, **kwargs):
        super(LogEntryAdmin, self).__init__(*args, **kwargs)
        self.list_display_links = (None, )

C'est une sorte de mix entre les deux réponses.

Si vous venez de le faire self.list_display_links = () il montrera le lien, de toute façon parce que le template-tag code (templatetags/admin_list.py) vérifie à nouveau si la liste est vide.

58
répondu Federico 2012-12-03 04:20:48

faire ceci correctement nécessite deux étapes:

  • masquer le lien d'édition, pour que personne ne trébuche sur la page de détail (changer la vue) par erreur.
  • Modifier la modifier la vue de rediriger retour à la liste afficher.

la deuxième partie est importante: si vous ne le faites pas, les gens pourront tout de même accéder à la vue change en entrant directement une URL (ce que vous ne voulez probablement pas). Cela est étroitement lié à ce que L'OWASP terme an "Unsecure Direct Object Reference".

dans le cadre de cette réponse je construirai un ReadOnlyMixin classe qui peut être utilisé pour fournir toutes les fonctionnalités montré.

Cacher le lien D'édition

Django 1.7 rend cela vraiment facile: vous venez de mettre list_display_linksNone.

class ReadOnlyMixin(): # Add inheritance from "object" if using Python 2
    list_display_links = None

Django 1.6 (et probablement plus tôt) ne rend pas cela si simple. Beaucoup de réponses à cette question ont suggéré primordial __init__ afin de mettre en list_display_links après que l'objet a été construit, mais cela le rend plus difficile à réutiliser (nous ne pouvons Outrepasser le constructeur qu'une seule fois).

je pense que la meilleure option est de les remplacer Django get_list_display_links méthode comme suit:

def get_list_display_links(self, request, list_display):
    """
    Return a sequence containing the fields to be displayed as links
    on the changelist. The list_display parameter is the list of fields
    returned by get_list_display().

    We override Django's default implementation to specify no links unless
    these are explicitly set.
    """
    if self.list_display_links or not list_display:
        return self.list_display_links
    else:
        return (None,)

cela rend notre mixin facile à utiliser: il cache le lien d'édition par défaut mais nous permet de l'ajouter de nouveau si nécessaire pour une vue d'administrateur particulière.

redirection vers la vue Liste

nous pouvons changer le comportement du page de détail (change view) en remplaçant le change_view méthode. Voici une extension à la technique suggérée par Chris Pratt qui trouve automatiquement la bonne page:

enable_change_view = False

def change_view(self, request, object_id, form_url='', extra_context=None):
    """
    The 'change' admin view for this model.

    We override this to redirect back to the changelist unless the view is
    specifically enabled by the "enable_change_view" property.
    """
    if self.enable_change_view:
        return super(ReportMixin, self).change_view(
            request,
            object_id,
            form_url,
            extra_context
        )
    else:
        from django.core.urlresolvers import reverse
        from django.http import HttpResponseRedirect

        opts = self.model._meta
        url = reverse('admin:{app}_{model}_changelist'.format(
            app=opts.app_label,
            model=opts.model_name,
        ))
        return HttpResponseRedirect(url)

là Encore, c'est personnalisable par basculement enable_change_viewTrue vous pouvez revenir sur la page des détails.

supprimer le " Ajouter ITEM" Bouton

enfin, vous pourriez vouloir outrepasser les méthodes suivantes afin d'empêcher les gens d'ajouter ou de supprimer de nouvelles article.

def has_add_permission(self, request):
    return False

def has_delete_permission(self, request, obj=None):
    return False

Ces modifications suivantes sont apportées:

  • désactiver le " Add item" bouton
  • empêcher les gens d'ajouter directement des articles en ajoutant /add à L'URL
  • prévenir les supprimer en vrac

enfin vous pouvez supprimer le " Delete selected éléments" l'action en modifiant l' actions paramètre.

Mettre

Voici la version complète mixin:

from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect

class ReadOnlyMixin(): # Add inheritance from "object" if using Python 2

    actions = None

    enable_change_view = False

    def get_list_display_links(self, request, list_display):
        """
        Return a sequence containing the fields to be displayed as links
        on the changelist. The list_display parameter is the list of fields
        returned by get_list_display().

        We override Django's default implementation to specify no links unless
        these are explicitly set.
        """
        if self.list_display_links or not list_display:
            return self.list_display_links
        else:
            return (None,)

    def change_view(self, request, object_id, form_url='', extra_context=None):
        """
        The 'change' admin view for this model.

        We override this to redirect back to the changelist unless the view is
        specifically enabled by the "enable_change_view" property.
        """
        if self.enable_change_view:
            return super(ReportMixin, self).change_view(
                request,
                object_id,
                form_url,
                extra_context
            )
        else:
            opts = self.model._meta
            url = reverse('admin:{app}_{model}_changelist'.format(
                app=opts.app_label,
                model=opts.model_name,
            ))
            return HttpResponseRedirect(url)

    def has_add_permission(self, request):
        return False

    def has_delete_permission(self, request, obj=None):
        return False
30
répondu simpleigh 2014-10-22 18:50:37

en tant qu'utilisateur, omat, mentionné dans un commentaire ci-dessus, toute tentative de simplement supprimer les liens n'empêche pas les utilisateurs d'accéder encore à la page de changement manuellement. Cependant, cela aussi est assez facile à corriger:

class MyModelAdmin(admin.ModelAdmin)
    # Other stuff here
    def change_view(self, request, obj=None):
        from django.core.urlresolvers import reverse
        from django.http import HttpResponseRedirect
        return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist'))
18
répondu Chris Pratt 2011-04-29 21:04:02

à Django 1.7 et plus tard, vous pouvez faire

class HitAdmin(admin.ModelAdmin):
    list_display_links = None
10
répondu Blaise 2016-09-26 09:19:41

Dans votre modèle de set admin:

list_display_links = (None,)

ça devrait le faire. (Fonctionne en tout cas au 1.1.1.)

Lien vers les docs: list_display_links

5
répondu Josh Ourisman 2018-02-12 00:20:48

il n'y a pas de façon supportée de faire cela.

en Regardant le code, il semble qu'il définit automatiquement ModelAdmin.list_display_links au premier élément si vous ne le mettez à rien. Ainsi, la façon la plus simple pourrait être d'Outrepasser le __init__ dans votre ModelAdmin sous-classe d'annuler cet attribut lors de l'initialisation:

class HitAdmin(admin.ModelAdmin):
    list_display = ('user','ip','user_agent','hitcount')
    search_fields = ('ip','user_agent')
    date_hierarchy = 'created'

    def __init__(self, *args, **kwargs):
        super(HitAdmin, self).__init__(*args, **kwargs)
        self.list_display_links = []

cela semble fonctionner, après un test très superficiel. Je ne peux pas garantir qu'il ne cassera rien ailleurs, ou qu'il ne sera pas brisé par de futurs changements à Django, si.

Modifier après le commentaire:

Pas besoin de patch la source, cela devrait fonctionner:

    def __init__(self, *args, **kwargs):
        if self.list_display_links:
            unset_list_display = True
        else:
            unset_list_display = False
        super(HitAdmin, self).__init__(*args, **kwargs)
        if unset_list_display:
            self.list_display_links = []

mais je doute fortement que N'importe quel patch soit accepté dans Django, puisque cela casse quelque chose que le code fait explicitement pour le moment.

2
répondu Daniel Roseman 2009-10-25 08:25:16

Juste pour les notes, vous pouvez modifier changelist_view:

class SomeAdmin(admin.ModelAdmin):
    def changelist_view(self, request, extra_context=None):
        self.list_display_links = (None, )
        return super(SomeAdmin, self).changelist_view(request, extra_context=None)

Cela fonctionne très bien pour moi.

2
répondu tinti 2013-03-22 08:16:51

Vous pourriez également être ridiculement hacky à ce sujet (si vous ne voulez pas de chichi avec prépondérant init) et fournir une valeur pour le premier élément qui ressemble fondamentalement à ceci:

</a>My non-linked value<a>

je sais, je sais, pas très jolie, mais peut-être moins d'anxiété à propos de casser quelque chose ailleurs puisque tout ce que nous faisons est de changer la marge.

voici un exemple de code sur la façon dont cela fonctionne:

class HitAdmin(admin.ModelAdmin):
    list_display = ('user_no_link','ip','user_agent','hitcount')

    def user_no_link(self, obj):
        return u'</a>%s<a>' % obj
    user_no_link.allow_tags = True
    user_no_link.short_description = "user"

note latérale: vous pouvez aussi améliorer la lisibilité de la sortie (puisque vous ne voulez pas qu'il soit un lien) en retournant return u'%s' % obj.get_full_name() qui pourrait être assez soigné selon votre cas d'utilisation.

1
répondu T. Stone 2009-10-24 21:26:40

avec django 1.6.2 vous pouvez faire comme ceci:

class MyAdmin(admin.ModelAdmin):

    def get_list_display_links(self, request, list_display):
        return []

il masquera tous les liens générés automatiquement.

1
répondu truease.com 2014-02-21 02:16:11