Отключить ссылку для редактирования объекта в админке django (только отображение списка)? - PullRequest
36 голосов
/ 24 октября 2009

В админке Django я хочу отключить ссылки, предоставленные на странице «выбрать элемент для изменения», чтобы пользователи не могли никуда пойти, чтобы отредактировать элемент. (Я собираюсь ограничить то, что пользователи могут делать с этим списком, набором выпадающих действий - без фактического редактирования полей).

Я вижу, что у Django есть возможность выбирать, какие поля отображают ссылку , однако я не вижу, как я могу получить нет из них.

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

Есть идеи, как получить список объектов без ссылок для редактирования?

Ответы [ 10 ]

59 голосов
/ 31 декабря 2009

Я хотел, чтобы просмотр журнала был только в виде списка.

У меня это работает так:

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

Это своего рода смесь обоих ответов.

Если вы просто сделаете self.list_display_links = (), он покажет ссылку, во всяком случае, потому что код template-tag (templatetags / admin_list.py) снова проверяет, пуст ли список.

31 голосов
/ 22 октября 2014

Чтобы сделать это правильно, нужно выполнить два шага:

  • Скрыть ссылку редактирования, чтобы никто не оступился на странице сведений (изменить вид) по ошибке.
  • Измените представление изменения, чтобы перенаправить обратно в представление списка.

Вторая часть важна: если вы этого не сделаете, люди все равно смогут получить доступ к представлению изменений, введя URL-адрес напрямую (что, по-видимому, вам не нужно). Это тесно связано с тем, что OWASP называет «Небезопасная прямая ссылка на объект» .

В рамках этого ответа я создам класс ReadOnlyMixin, который можно использовать для обеспечения всех отображаемых функций.

Скрытие ссылки редактирования

Django 1.7 делает это действительно легко: вы просто устанавливаете list_display_links на None.

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

Django 1.6 (и, вероятно, ранее) не делает это настолько простым. Довольно много ответов на этот вопрос предложили переопределить __init__, чтобы установить list_display_links после того, как объект был построен, но это усложняет повторное использование (мы можем переопределить конструктор только один раз).

Я думаю, что лучший вариант - переопределить метод get_list_display_links Джанго следующим образом:

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

Это делает наш миксин простым в использовании: он скрывает ссылку редактирования по умолчанию, но позволяет нам добавить его обратно, если это требуется для определенного вида администратора.

Перенаправление на просмотр списка

Мы можем изменить поведение страницы сведений (изменить представление), переопределив метод change_view. Вот расширение техники, предложенной Крисом Праттом, которая автоматически находит нужную страницу:

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)

Опять же, это можно настраивать - переключая enable_change_view на True, вы можете снова включить страницу сведений.

Снятие кнопки «Добавить ITEM »

Наконец, вы можете переопределить следующие методы, чтобы люди не могли добавлять или удалять новые элементы.

def has_add_permission(self, request):
    return False

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

Эти изменения будут:

  • отключить кнопку «Добавить элемент »
  • запретить людям добавлять элементы напрямую, добавив /add к URL-адресу
  • предотвратить массовое удаление

Наконец, вы можете удалить действие «Удалить выбранные items », изменив параметр actions.

Собираем все вместе

Вот готовый миксин:

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
18 голосов
/ 30 апреля 2011

Как пользователь omat, упомянутый в комментарии выше, любая попытка простого удаления ссылок не мешает пользователям по-прежнему обращаться к странице изменений вручную. Однако это тоже достаточно легко исправить:

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'))
12 голосов
/ 13 марта 2014

В Django 1.7 и более поздних версиях вы можете сделать

class HitAdmin(admin.ModelAdmin):
    list_display_links = None
7 голосов
/ 02 ноября 2009

В вашей модели админ установлен:

list_display_links = (None,)

Это должно сделать это. (В любом случае работает в 1.1.1.)

Ссылка на документы: list_display_links

2 голосов
/ 22 марта 2013

Только для заметок, вы можете изменить 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)

Это прекрасно работает для меня.

2 голосов
/ 25 октября 2009

Нет поддерживаемого способа сделать это.

Глядя на код, кажется, что он автоматически устанавливает ModelAdmin.list_display_links на первый элемент, если вы ничего не установите для него. Поэтому самым простым способом может быть переопределение метода __init__ в вашем подклассе ModelAdmin для сброса этого атрибута при инициализации:

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

Это похоже на работу после очень краткого теста. Я не могу гарантировать, что это не сломает ничего в другом месте, или что оно не будет сломано будущими изменениями в Django.

Редактировать после комментария :

Нет необходимости исправлять источник, это будет работать:

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

Но я очень сомневаюсь, что любой патч будет принят в Django, поскольку это нарушает то, что код явно делает в данный момент.

1 голос
/ 06 марта 2019

просто напишите list_display_links = None в вашем админе

1 голос
/ 21 февраля 2014

с django 1.6.2 вы можете сделать так:

class MyAdmin(admin.ModelAdmin):

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

будет скрывать все автоматически сгенерированные ссылки.

1 голос
/ 25 октября 2009

Вы также можете быть смехотворно хакать об этом (если вы не хотите возиться с переопределением <strong>init</strong>) и предоставить значение для первого элемента, который в основном выглядит это:

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

Я знаю, знаю, не очень красиво, но, возможно, меньше беспокоюсь о том, чтобы сломать что-то в другом месте, так как все, что мы делаем, это меняем разметку.

Вот пример кода о том, как это работает:

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"

Примечание: вы также можете улучшить читабельность вывода (так как вы не хотите, чтобы это была ссылка), вернув return u'%s' % obj.get_full_name(), что может быть довольно аккуратно в зависимости от вашего варианта использования.

...