python 在 Django 管理屏幕中删除“添加另一个”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1721037/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 22:57:02  来源:igfitidea点击:

Remove "add another" in Django admin screen

pythondjangodjango-admin

提问by Hyman Ha

Whenever I'm editing object A with a foreign key to object B, a plus option "add another" is available next to the choices of object B. How do I remove that option?

每当我使用对象 B 的外键编辑对象 A 时,对象 B 的选项旁边都会有一个加号选项“添加另一个”。如何删除该选项?

I configured a user without rights to add object B. The plus sign is still available, but when I click on it, it says "Permission denied". It's ugly.

我配置了一个没有权限添加对象 B 的用户。加号仍然可用,但是当我点击它时,它显示“权限被拒绝”。它很丑。

I'm using Django 1.0.2

我正在使用 Django 1.0.2

采纳答案by T. Stone

DEPRECATED ANSWER

已弃用的答案

Django has since made this possible.

Django 使这成为可能。



Have you considered instead using CSS to simply not show the button? Maybe that's a little too hacky.

您是否考虑过使用 CSS 来简单地不显示按钮?也许这有点太hacky了。

This is untested, but I'm thinking...

这是未经测试的,但我在想......

no-addanother-button.css

no-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 for doing this -- Media as a static definition

用于执行此操作的 Django Doc --媒体作为静态定义

Note/Edit:The documentation says the files will be prepended with the MEDIA_URL but in my experimentation it isn't. Your mileage may vary.

注意/编辑:文档说文件将带有 MEDIA_URL,但在我的实验中它不是。你的旅费可能会改变。

If you find this is the case for you, there's a quick fix for this...

如果您发现这种情况适合您,则可以快速解决此问题...

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',) }

回答by pistache

(stop upvoting this wrong answer!!!)

(停止支持这个错误的答案!!!)

ERRATA: This answer is basically wrong, and does not answer OP's question. See below.

勘误表:这个答案基本上是错误的,并没有回答 OP 的问题。见下文。

(this is only applicable to inline forms, not foreign key fields as OP asked)

Simpler solution, no CSS hack, no editing Django codebase :

Add this to your Inline class :

max_num=0

(这仅适用于内联表单,不适用于 OP 要求的外键字段)

更简单的解决方案,没有 CSS hack,没有编辑 Django 代码库:

将此添加到您的内联类:

max_num=0

UPDATE

更新

This does not answer OP's question, and is only useful to hide the "add related" button for inline forms, and not foreign keys as requested.

这不能回答 OP 的问题,并且仅用于隐藏内联表单的“添加相关”按钮,而不是按要求隐藏外键。

When I wrote this answer, IIRC the accepted answer hide both, which is why I got confused.

当我写这个答案时,IIRC 接受的答案隐藏了两者,这就是我感到困惑的原因。

The following links seems to provide a solution (though hiding using CSS seems the most feasible things to do, especially if the "add another" buttons of FKs in inline forms) :

以下链接似乎提供了一个解决方案(尽管使用 CSS 隐藏似乎是最可行的方法,尤其是在内联表单中 FK 的“添加另一个”按钮时):

Django 1.7 removing Add button from inline form

Django 1.7 从内联表单中删除添加按钮

回答by Sudipta

Though most of the solutions mentioned here work, there is another cleaner way of doing it. Probably it was introduced in a later version of Django, after the other solutions were presented. (I'm presently using Django 1.7)

尽管这里提到的大多数解决方案都有效,但还有另一种更简洁的方法。可能它是在 Django 的更高版本中引入的,在其他解决方案提出之后。(我目前正在使用 Django 1.7)

To remove the "Add another" option,

要删除“添加另一个”选项,

class ... #(Your inline class)

    def has_add_permission(self, request):
        return False

Similarly if you want to disable "Delete?" option, add the following method in Inline class.

同样,如果您想禁用“删除?” 选项,在 Inline 类中添加以下方法。

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

回答by Endophage

N.B. Works for DJango 1.5.2 and possibly older. The can_add_relatedproperty appearedaround 2 years ago.

NB 适用于 DJango 1.5.2 和可能更早的版本。该can_add_related属性出现在大约 2 年前。

The best way I've found is to override your ModelAdmin's get_form function. In my case I wanted to force the author of a post to be the currently logged in user. Code below with copious comments. The really important bit is the setting of widget.can_add_related:

我发现的最好方法是覆盖 ModelAdmin 的 get_form 函数。就我而言,我想强制帖子的作者成为当前登录的用户。下面的代码带有大量注释。真正重要的一点是设置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

The interesting part of making the changes here in get_formis that author.widgetis an instance of django.contrib.admin.widgets.RelatedFieldWidgetWrapperwhere as if you try and make changes in one of the formfield_for_xxxxxfunctions, the widget is an instance of the actual form widget, in this typical ForeignKey case it's a django.forms.widgets.Select.

在这里进行更改的有趣的部分get_form是,author.widget是的一个实例,django.contrib.admin.widgets.RelatedFieldWidgetWrapper在那里,如果你尝试,使变化的一个formfield_for_xxxxx功能,小部件的实际表单控件的一个实例,它是一个典型的这样的ForeignKey情况django.forms.widgets.Select

回答by cethegeek

Look at django.contrib.admin.options.pyand check out the BaseModelAdminclass, formfield_for_dbfieldmethod.

查看django.contrib.admin.options.py并检查BaseModelAdmin类,formfield_for_dbfield方法。

You will see this:

你会看到这个:

# 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)

I think your best bet is create subclass of ModelAdmin(which in turn is a subclass of BaseModelAdmin), base your model on that new class, override formfield_fo_dbfieldand make it so that it won't/or will conditionally wrap the widget in RelatedFieldWidgetWrapper.

我认为您最好的选择是创建的子类ModelAdmin(它又是 的子类BaseModelAdmin),将您的模型基于该新类,覆盖formfield_fo_dbfield并使其不会/或将有条件地将小部件包装在RelatedFieldWidgetWrapper.

One could argue that if you have a user that doesn't have rights to adding related objects, the RelatedFieldWidgetWrappershould not display the add link? Maybe this is something that is deserving of mention in Django trac?

有人可能会争辩说,如果您的用户无权添加相关对象,则RelatedFieldWidgetWrapper不应显示添加链接?也许这是Django trac 中值得一提的东西?

回答by Slipstream

I use the following approaches for Formand InlineForm

我对FormInlineForm使用以下方法

Django 2.0, Python 3+

Django 2.0,Python 3+

Form

形式

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  

Inline 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

回答by djvg

The answer by @Slipstreamshows howto implement the solution, viz. by overriding the attributes for the formfield's widget, but, in my opinion, get_formis not the most logical place to do this.

@Slipstream的答案显示了如何实施解决方案,即。通过覆盖表单域小部件的属性,但在我看来,get_form这不是最合乎逻辑的地方。

The answer by @cethegeekshows whereto implement the solution, viz. in an extension of formfield_for_dbfield, but does not provide an explicit example.

@cethegeek的答案显示了在哪里实施解决方案,即。在 的扩展中formfield_for_dbfield,但没有提供明确的例子。

Why use formfield_for_dbfield? Its docstringsuggests that it is the designated hook for messing with form fields:

为什么使用formfield_for_dbfield?它的文档字符串表明它是用于处理表单字段的指定钩子:

Hook for specifying the form Field instance for a given database Field instance.

用于为给定数据库 Field 实例指定表单 Field 实例的挂钩。

It also allows for (slightly) cleaner and clearer code, and, as a bonus, we can easily set additional form Fieldattributes, such as initialvalue and/or disabled(example here), by adding them to the kwargs(before calling super).

它还允许(稍微)更清晰和更清晰的代码,作为奖励,我们可以通过将它们添加到(在调用之前)轻松设置额外的表单Field属性,例如initial值和/或disabled此处的示例)。kwargssuper

So, combining the two answers (assuming the OP's models are ModelAand ModelB, and the ForeignKeymodel field is named b):

因此,结合两个答案(假设 OP 的模型是ModelAand 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)

NOTE: If the ForeignKeymodel field has on_delete=models.CASCADE, the can_delete_relatedattribute is Falseby default, as can be seen in the sourcefor RelatedFieldWidgetWrapper.

注意:如果ForeignKey模型字段on_delete=models.CASCADE,该can_delete_related属性是False在默认情况下,如能在可以看到RelatedFieldWidgetWrapper

回答by Rahmat Nazali S

I'm using Django 2.x and I think I found best solution, at least for my case.

我正在使用 Django 2.x,我想我找到了最好的解决方案,至少对我来说是这样。

The HTML file to the "Save and Add Another" button is on your_python_installation\Lib\site-packages\django\contrib\admin\templates\admin\subtmit_line.html.

“保存并添加另一个”按钮的 HTML 文件位于your_python_installation\Lib\site-packages\django\contrib\admin\templates\admin\subtmit_line.html.

  1. Copy that html file and paste to your project like so your_project\templates\admin\submit_line.html.
  2. Open it and comment/delete the button code as desired:
  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 %}#}

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

I know this problem is already answered. But maybe someone in the future have a similar case with me.

我知道这个问题已经得到解答。但也许将来有人和我有类似的情况。

回答by mirek

Based on cethegeek answer I made this:

基于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

回答by sgiob

django.contrib.admin.widgets.py

django.contrib.admin.widgets.py

(Django Install Dir)/django/contrib/admin/widgets.py: Comment everything between Line 239 & Line 244:

(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')))