python tutorial Disabilita il link per modificare l'oggetto nell'amministratore di django(solo sulla lista di visualizzazione)?




tutorial django project (8)

Nell'amministratore di Django, desidero disattivare i collegamenti forniti nella pagina "Seleziona elemento da modificare" in modo che gli utenti non possano andare da nessuna parte per modificare l'elemento. (Ho intenzione di limitare ciò che gli utenti possono fare con questo elenco a una serie di azioni a discesa - nessuna modifica effettiva dei campi).

Vedo che Django ha la possibilità di scegliere quali campi visualizzare il collegamento , tuttavia, non riesco a vedere come posso non averne nessuno .

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

Qualche idea su come ottenere il mio elenco oggetti senza alcun link da modificare?


Volevo un visualizzatore di log solo come lista.

Ho funzionato così:

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, )

È una sorta di mix tra entrambe le risposte.

Se fai solo self.list_display_links = () mostrerà il link, comunque perché il codice del template-tag (templatetags / admin_list.py) controlla ancora per vedere se la lista è vuota.


Fare ciò correttamente richiede due passaggi:

  • Nascondi il link di modifica, quindi nessuno inciampa nella pagina dei dettagli (cambia vista) per errore.
  • Modifica la vista di modifica per reindirizzare nuovamente alla visualizzazione elenco.

La seconda parte è importante: se non lo fai, le persone saranno comunque in grado di accedere alla vista delle modifiche inserendo direttamente un URL (che presumibilmente non vuoi). Questo è strettamente correlato a ciò che OWASP definisce un "Riferimento di oggetti diretti non sicuri" .

Come parte di questa risposta, costruirò una classe ReadOnlyMixin che può essere utilizzata per fornire tutte le funzionalità mostrate.

Nascondere il link Modifica

Django 1.7 lo rende davvero semplice: basta impostare list_display_links su None .

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

Django 1.6 (e presumibilmente prima) non lo rende così semplice. Un sacco di risposte a questa domanda hanno suggerito di sovrascrivere __init__ per impostare list_display_links dopo che l'oggetto è stato costruito, ma questo rende più difficile il riutilizzo (possiamo solo scavalcare il costruttore una volta).

Penso che un'opzione migliore sia quella di sovrascrivere il metodo get_list_display_links di Django come segue:

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,)

Questo rende il nostro mixin facile da usare: nasconde il link di modifica di default ma ci consente di aggiungerlo di nuovo se richiesto per una particolare vista di amministrazione.

Reindirizzamento alla vista elenco

Possiamo cambiare il comportamento della pagina dei dettagli (cambia vista) sovrascrivendo il metodo change_view . Ecco un'estensione della tecnica suggerita da Chris Pratt che trova automaticamente la pagina giusta:

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)

Anche in questo caso è personalizzabile: attivando la funzione enable_change_view su True è possibile riattivare la pagina dei dettagli.

Rimozione del pulsante "Aggiungi ELEMENTO "

Infine, potresti voler sovrascrivere i seguenti metodi per evitare che le persone aggiungano o cancellino nuovi elementi.

def has_add_permission(self, request):
    return False

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

Queste modifiche:

  • disattiva il pulsante "Aggiungi elemento "
  • evitare che le persone aggiungano direttamente elementi aggiungendo /add all'URL
  • prevenire l'eliminazione di massa

Infine puoi rimuovere l'azione "Elimina oggetti selezionati" modificando il parametro delle actions .

Mettere tutto insieme

Ecco il mixin completato:

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

con django 1.6.2 puoi fare così:

class MyAdmin(admin.ModelAdmin):

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

nasconderà tutti i collegamenti generati automaticamente.


In Django 1.7 e versioni successive, puoi farlo

class HitAdmin(admin.ModelAdmin):
    list_display_links = None

Non c'è un modo supportato per farlo.

Guardando il codice, sembra che esso imposta automaticamente ModelAdmin.list_display_links sul primo elemento se non lo si imposta su nulla. Quindi il modo più semplice potrebbe essere quello di sovrascrivere il metodo __init__ nella sottoclasse ModelAdmin per ModelAdmin l' ModelAdmin all'inizializzazione:

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 = []

Questo sembra funzionare, dopo un test molto superficiale. Non posso garantire che non si romperà nulla altrove, o che non sarà rotto da future modifiche a Django, però.

Modifica dopo commento :

Non c'è bisogno di patchare la fonte, questo funzionerebbe:

    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 = []

Ma dubito fortemente che qualsiasi patch possa essere accettata in Django, poiché questo rompe qualcosa che il codice fa esplicitamente al momento.


Solo per le note, puoi modificare 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)

Questo funziona bene per me.


Potresti anche essere ridicolmente hackerato su di esso (se non volessi litigare con init sovrascritto) e fornire un valore per il primo elemento che sostanzialmente assomiglia a questo:

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

Lo so, lo so, non molto carina, ma forse meno ansia di rompere qualcosa da qualche altra parte dato che tutto quello che stiamo facendo è cambiare il markup.

Ecco alcuni esempi di codice su come funziona:

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"

Nota a margine: puoi anche migliorare la leggibilità dell'output (dato che non vuoi che sia un link) restituendo return u'%s' % obj.get_full_name() che potrebbe essere abbastanza carino a seconda del tuo caso d'uso.


Nel set di amministratori del modello:

list_display_links = (None,)

Questo dovrebbe farlo. (Funziona comunque in 1.1.1).

Link a documenti: list_display_links





modeladmin