在Django管理界面中删除“添加另一个”

时间:2009-11-12 09:35:37

标签: python django django-admin

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

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

我正在使用Django 1.0.2

10 个答案:

答案 0 :(得分:48)

(停止提出这个错误答案!)

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

  

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

     

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

     

将此添加到您的Inline类:

     

max_num=0

<强>更新

这不符合OP的问题,只对隐藏内联表单的“添加相关”按钮有用,而不是按要求隐藏外键。

当我写这个答案时,IIRC接受的答案都隐藏了,这就是为什么我感到困惑。

以下链接似乎提供了一个解决方案(虽然使用CSS隐藏似乎是最可行的事情,特别是如果在内联表单中添加FK的“另一个”按钮):

Django 1.7 removing Add button from inline form

答案 1 :(得分:20)

虽然这里提到的大多数解决方案都有效,但还有另一种更清洁的方法。在提出其他解决方案之后,可能是在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

答案 2 :(得分:13)

N.B。适用于DJango 1.5.2,可能更旧。大约2年前can_add_related属性appeared

我发现的最好方法是覆盖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

get_form中进行更改的有趣部分是author.widgetdjango.contrib.admin.widgets.RelatedFieldWidgetWrapper的一个实例,就好像您尝试在其中一个formfield_for_xxxxx中进行更改一样函数,小部件是实际表单小部件的一个实例,在这个典型的ForeignKey情况下,它是django.forms.widgets.Select

答案 3 :(得分:6)

查看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中值得一提的东西?

答案 4 :(得分:4)

弃用的答案

Django已经使这成为可能。


您是否考虑过使用CSS而不显示按钮?也许这有点太hacky。

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

无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 as a static definition

注意/编辑:文档说文件将以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',) }

答案 5 :(得分:4)

我对 Form 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

答案 6 :(得分:1)

我使用的是Django 2.x,我认为我找到了最佳的解决方案,至少就我而言。

“ {Save and Add Another”(保存并添加另一个)按钮的HTML文件位于your_python_installation\Lib\site-packages\django\contrib\admin\templates\admin\subtmit_line.html上。

  1. 像这样your_project\templates\admin\submit_line.html那样复制该html文件并将其粘贴到您的项目中。
  2. 打开它并根据需要注释/删除按钮代码:

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

我知道此问题已得到解决。但是也许将来有人跟我有类似的情况。

答案 7 :(得分:1)

@Slipstream的答案显示了如何实现解决方案,即。通过覆盖formfield的小部件的属性,但是我认为get_form并不是最合逻辑的地方。

@cethegeek的答案显示了 where 用于实施解决方案,即。 formfield_for_dbfield的扩展名中,但未提供明确的示例。

为什么使用formfield_for_dbfield?其docstring表示它是用于弄乱表单字段的指定钩子:

  

为指定的数据库Field实例指定表单Field实例的钩子。

它还允许(稍微)更清晰的代码,并且,作为奖励,我们可以轻松设置其他形式Field attributes,例如initial值和/或{ {1}(例如here),方法是将它们添加到disabled中(在调用kwargs之前)。

因此,结合两个答案(假设OP的模型为superModelA,并且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,如{ {1}}。

答案 8 :(得分:0)

根据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

答案 9 :(得分:-1)

django.contrib.admin.widgets.py

(Django Install Dir)/django/contrib/admin/widgets.py:评论239行和&amp;之间的所有内容。第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')))