Удалите «добавить еще» на экране администратора Django.
-
19-09-2019 - |
Вопрос
Всякий раз, когда я редактирую объект A с помощью внешнего ключа к объекту B, рядом с вариантами выбора объекта B доступен плюс «добавить еще».Как мне удалить эту опцию?
Я настроил пользователя без прав на добавление объекта Б.Знак плюс по-прежнему доступен, но когда я нажимаю на него, появляется сообщение «В доступе отказано».Это ужасно.
Я использую Джанго 1.0.2
Решение
УСТАРЕВШИЙ ОТВЕТ
С тех пор Django сделал это возможным.
Рассматривали ли вы вместо этого использование CSS, чтобы просто не показывать кнопку?Возможно, это слишком хакерски.
Это не проверено, но я думаю...
нет-addanother-button.css
#_addanother { display: none }
admin.py
class YourAdmin(admin.ModelAdmin):
# ...
class Media:
# edit this path to wherever
css = { 'all' : ('css/no-addanother-button.css',) }
Джанго Док за это - СМИ как статическое определение
Примечание/Изменить: В документации говорится, что к файлам будет добавлен MEDIA_URL, но в моих экспериментах это не так.Ваш пробег может отличаться.
Если вы обнаружите, что это ваш случай, есть быстрое решение этой проблемы...
class YourAdmin(admin.ModelAdmin):
# ...
class Media:
from django.conf import settings
media_url = getattr(settings, 'MEDIA_URL', '/media/')
# edit this path to wherever
css = { 'all' : (media_url+'css/no-addanother-button.css',) }
Другие советы
(прекратите голосовать за этот неправильный ответ!!!)
ОШИБКИ :Этот ответ в основном неверен и не отвечает на вопрос ОП.См. ниже.
(это применимо только к встроенным формам, а не к полям внешнего ключа, как просил ОП)
Более простое решение, без взлома CSS и редактирования кодовой базы Django:
Добавьте это в свой встроенный класс:
max_num=0
ОБНОВЛЯТЬ
Это не отвечает на вопрос ОП и полезно только для скрытия кнопки «Добавить связанную» для встроенных форм, а не внешних ключей по запросу.
Когда я писал этот ответ, принятый ответ IIRC скрывал оба, поэтому я запутался.
Следующие ссылки, по-видимому, предоставляют решение (хотя скрытие с помощью CSS кажется наиболее возможным, особенно если кнопки «добавить еще» FK во встроенных формах):
Хотя большинство упомянутых здесь решений работают, есть другой, более чистый способ сделать это.Вероятно, это было введено в более поздней версии Django, после того, как были представлены другие решения.(Сейчас я использую Django 1.7)
Чтобы удалить опцию «Добавить еще»,
class ... #(Your inline class)
def has_add_permission(self, request):
return False
Точно так же, если вы хотите отключить "Delete?" Опция, добавьте следующий метод в класс встроенного.
def has_delete_permission(self, request, obj=None):
return False
Н.Б.Работает для DJango 1.5.2 и, возможно, старше.А can_add_related
свойство появился около 2 лет назад.
Лучший способ, который я нашел, — это переопределить функцию get_form вашего ModelAdmin.В моем случае я хотел, чтобы автор сообщения был пользователем, вошедшим в систему в данный момент.Код ниже с обильными комментариями.Действительно важным моментом является настройка widget.can_add_related
:
def get_form(self,request, obj=None, **kwargs):
# get base form object
form = super(BlogPostAdmin,self).get_form(request, obj, **kwargs)
# get the foreign key field I want to restrict
author = form.base_fields["author"]
# remove the green + by setting can_add_related to False on the widget
author.widget.can_add_related = False
# restrict queryset for field to just the current user
author.queryset = User.objects.filter(pk=request.user.pk)
# set the initial value of the field to current user. Redundant as there will
# only be one option anyway.
author.initial = request.user.pk
# set the field's empty_label to None to remove the "------" null
# field from the select.
author.empty_label = None
# return our now modified form.
return form
Самая интересная часть внесения изменений здесь, в get_form
в том, что author.widget
является примером django.contrib.admin.widgets.RelatedFieldWidgetWrapper
как будто вы пытаетесь внести изменения в один из formfield_for_xxxxx
функции, виджет является экземпляром фактического виджета формы, в этом типичном случае ForeignKey это django.forms.widgets.Select
.
Посмотри на django.contrib.admin.options.py
и проверьте BaseModelAdmin
сорт, formfield_for_dbfield
метод.
Вы увидите это:
# For non-raw_id fields, wrap the widget with a wrapper that adds
# extra HTML -- the "add other" interface -- to the end of the
# rendered output. formfield can be None if it came from a
# OneToOneField with parent_link=True or a M2M intermediary.
if formfield and db_field.name not in self.raw_id_fields:
formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site)
Я думаю, что лучше всего создать подкласс ModelAdmin
(который, в свою очередь, является подклассом BaseModelAdmin
), основывайте свою модель на этом новом классе, переопределите formfield_fo_dbfield
и сделайте так, чтобы он не/или условно не переносил виджет в RelatedFieldWidgetWrapper
.
Можно возразить, что если у вас есть пользователь, у которого нет прав на добавление связанных объектов, RelatedFieldWidgetWrapper
не должна отображаться ссылка на добавление?Возможно, это то, что заслуживает упоминания в Джанго трек?
Я использую следующие подходы для Форма и ИнлайнФорм
Джанго 2.0, Питон 3+
Форма
class MyModelAdmin(admin.ModelAdmin):
#...
def get_form(self,request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
user = form.base_fields["user"]
user.widget.can_add_related = False
user.widget.can_delete_related = False
user.widget.can_change_related = False
return form
Встроенная форма
class MyModelInline(admin.TabularInline):
#...
def get_formset(self, request, obj=None, **kwargs):
formset = super().get_formset(request, obj, **kwargs)
user = formset.form.base_fields['user']
user.widget.can_add_related = False
user.widget.can_delete_related = False
user.widget.can_change_related = False
return formset
Ответ от @Slipstream шоу как реализовать решение, т.переопределив атрибуты виджета поля формы, но, на мой взгляд, get_form
это не самое логичное место для этого.
Ответ от @cethegeek шоу где реализовать решение, т.в расширении formfield_for_dbfield
, но не дает явного примера.
Зачем использовать formfield_for_dbfield
?Его строка документации предполагает, что это специальный крючок для работы с полями формы:
Хук для указания экземпляра поля формы для данного экземпляра поля базы данных.
Это также позволяет (немного) сделать код чище и понятнее, и, в качестве бонуса, мы можем легко установить дополнительную форму. Field
атрибуты, такой как initial
ценность и/или disabled
(пример здесь), добавив их в kwargs
(перед звонком super
).
Итак, объединив два ответа (при условии, что модели ОП ModelA
и ModelB
, и ForeignKey
Поле модели названо b
):
class ModelAAdmin(admin.ModelAdmin):
def formfield_for_dbfield(self, db_field, request, **kwargs):
# optionally set Field attributes here, by adding them to kwargs
formfield = super().formfield_for_dbfield(db_field, request, **kwargs)
if db_field.name == 'b':
formfield.widget.can_add_related = False
formfield.widget.can_change_related = False
formfield.widget.can_delete_related = False
return formfield
# Don't forget to register...
admin.site.register(ModelA, ModelAAdmin)
ПРИМЕЧАНИЕ:Если ForeignKey
поле модели имеет on_delete=models.CASCADE
, can_delete_related
атрибут False
по умолчанию, как видно из источник для RelatedFieldWidgetWrapper
.
Я использую Django 2.x и думаю, что нашел лучшее решение, по крайней мере, для моего случая.
HTML-файл для кнопки «Сохранить и добавить еще» включен. your_python_installation\Lib\site-packages\django\contrib\admin\templates\admin\subtmit_line.html
.
- Скопируйте этот html-файл и вставьте в свой проект следующим образом.
your_project\templates\admin\submit_line.html
. - Откройте его и закомментируйте/удалит код кнопки по желанию:
{#{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %}#}
Я знаю, что на эту проблему уже есть ответ.Но, возможно, у кого-то в будущем будет подобный случай со мной.
Основываясь на ответе cethegeek, я сделал это:
class SomeAdmin(admin.ModelAdmin):
form = SomeForm
def formfield_for_dbfield(self, db_field, **kwargs):
formfield = super(SomeAdmin, self).formfield_for_dbfield(db_field, **kwargs)
if db_field.name == 'some_m2m_field':
request = kwargs.pop("request", None)
formfield = self.formfield_for_manytomany(db_field, request, **kwargs) # for foreignkey: .formfield_for_foreignkey
wrapper_kwargs = {'can_add_related': False, 'can_change_related': False, 'can_delete_related': False}
formfield.widget = admin.widgets.RelatedFieldWidgetWrapper(
formfield.widget, db_field.remote_field, self.admin_site, **wrapper_kwargs
)
return formfield
django.contrib.admin.widgets.py
(Каталог установки Django)/django/contrib/admin/widgets.py:Прокомментируйте все, что находится между строкой 239 и строкой 244:
if rel_to in self.admin_site._registry: # If the related object has an admin interface:
# TODO: "id_" is hard-coded here. This should instead use the correct
# API to determine the ID dynamically.
output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
(related_url, name))
output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Add Another')))