Отключить ссылку для редактирования объекта в 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
Любые идеи о том, как получить список объектов без ссылок для редактирования?
Ответы
Ответ 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 []
он скроет все автоматически сгенерированные ссылки.