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

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

Я вижу, что 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

любой идеи как получить список объектов без каких-либо ссылок для редактирования?

9 ответов


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

Я заставил его работать так:

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


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

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

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

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

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

Джанго 1.7 делает это очень легко: вы просто list_display_links to 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'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,)

это делает наш 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 to True вы можете снова включить страницу сведений.

удаление "Add элемент"

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

def has_add_permission(self, request):
    return False

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

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

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

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


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

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

class HitAdmin(admin.ModelAdmin):
    list_display_links = None

в вашей модели admin set:

list_display_links = (None,)

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

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


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

глядя на код, кажется, что он автоматически устанавливает 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 = []

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

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

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

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


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

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


вы также может быть смешно hacky об этом (если вы не хотите возиться с переопределением 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() что может быть очень аккуратным в зависимости от вашего варианта использования.


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

class MyAdmin(admin.ModelAdmin):

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

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