Удалите «добавить еще» в админке Django - PullRequest
25 голосов
/ 12 ноября 2009

Всякий раз, когда я редактирую объект A с внешним ключом к объекту B, рядом с вариантами выбора объекта B доступна опция «добавить еще». Как удалить эту опцию?

Я настроил пользователя без прав на добавление объекта B. Знак плюс все еще доступен, но когда я нажимаю на него, он говорит: «Отказано в доступе». Это безобразно.

Я использую Django 1.0.2

Ответы [ 10 ]

48 голосов
/ 31 октября 2012

(перестаньте голосовать против этого неправильного ответа !!!)

ОШИБКИ : Этот ответ в основном неправильный и не отвечает на вопрос ОП. Смотри ниже.

(это применимо только к встроенным формам, а не к полям внешнего ключа, как указано в OP)

Более простое решение, без хака CSS, без редактирования кодовой базы Django:

Добавьте это в свой встроенный класс:

max_num=0

UPDATE

Это не отвечает на вопрос OP, а полезно только для того, чтобы скрыть кнопку «добавить связанный» для встроенных форм, а не внешние ключи в соответствии с запросом.

Когда я писал этот ответ, принятый ответ IIRC скрывал оба, поэтому я запутался.

Следующие ссылки, по-видимому, обеспечивают решение (хотя скрытие с помощью CSS кажется наиболее целесообразным, особенно если кнопки «добавить еще» FK в встроенных формах):

Django 1.7 удаляет кнопку «Добавить» из встроенной формы

21 голосов
/ 27 апреля 2015

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

Чтобы удалить опцию «Добавить еще»,

class ... #(Your inline class)

    def has_add_permission(self, request):
        return False

Аналогично, если вы хотите отключить «Удалить?» добавьте следующий метод в класс Inline.

    def has_delete_permission(self, request, obj=None):
        return False
13 голосов
/ 26 августа 2013

N.B. Работает на 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.

6 голосов
/ 12 ноября 2009

Посмотрите на 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 не должен отображать ссылку добавления? Может быть, это то, что заслуживает упоминания в Django trac ?

5 голосов
/ 20 июня 2018

Я использую следующие подходы для Форма и InlineForm

Django 2.0, Python 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
4 голосов
/ 13 ноября 2009

УСТАРЕЛИ ОТВЕТ

С тех пор Джанго сделал это возможным.


Рассматривали ли вы вместо этого использование 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',) }

Django Doc за это - Медиа как статическое определение

Примечание / редактирование: В документации сказано, что файлы будут дополнены 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',) }
1 голос
/ 15 января 2019

Ответ @ 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, как видно из source для RelatedFieldWidgetWrapper.

1 голос
/ 10 июля 2018

Я использую Django 2.x и думаю, что нашел лучшее решение, по крайней мере, для моего случая.

Файл HTML для кнопки «Сохранить и добавить еще» находится на your_python_installation\Lib\site-packages\django\contrib\admin\templates\admin\subtmit_line.html.

  1. Скопируйте этот HTML-файл и вставьте в свой проект, например, так: your_project\templates\admin\submit_line.html.
  2. Откройте его и прокомментируйте / удалите код кнопки по желанию:

{#{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %}#}

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

0 голосов
/ 14 мая 2018

На основании ответа 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
0 голосов
/ 07 ноября 2010

django.contrib.admin.widgets.py

(Django Install Dir) /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')))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...