Подтвердить что ты не робот

Отключить ссылку для редактирования объекта в django admin (только для отображения списка)?

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

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

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

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

4b9b3361

Ответ 1

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

Я начал работать так:

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) снова проверяет, нет ли списка пустым.

Ответ 2

Для этого необходимо выполнить два шага:

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

Вторая часть важна: если вы этого не сделаете, люди все равно смогут получить доступ к просмотру изменений, введя 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 после того, как объект был создан, но это затрудняет повторное использование (мы можем только переопределить конструктор один раз).

Я думаю, что лучший вариант - переопределить метод Django 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 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,)

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

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

Мы можем изменить поведение страницы подробностей (вид изменения), переопределив метод 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
  • предотвратить массовое удаление

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

Объединяя все это

Здесь завершено 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 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

Ответ 3

Как пользователь 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'))

Ответ 4

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

class HitAdmin(admin.ModelAdmin):
    list_display_links = None

Ответ 5

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

list_display_links = (None,)

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

Ответ 6

Существует не поддерживаемый способ сделать это.

Посмотрев на код, кажется, что он автоматически устанавливает 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, так как это нарушает то, что код явно делает на данный момент.

Ответ 7

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

Это отлично работает для меня.

Ответ 8

Вы также можете быть смешно взломать его (если вы не хотите суетиться с переопределением init) и предоставить значение для первого элемента, который в основном выглядит следующим образом:

</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(), который может быть немного опрятным в зависимости от вашего варианта использования.

Ответ 9

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

class MyAdmin(admin.ModelAdmin):

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

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