我配置了一个没有添加对象B权限的用户。加号仍然可用,但当我单击它时,它会显示“权限被拒绝”。这很丑。
我正在使用Django 1.0.2
max_num=0
(这仅适用于内联表单,而不是 OP 所要求的外键字段)
上面的答案只对内联表单中隐藏“添加相关”的按钮有用,不适用于外键。
当我写答案时,我记得接受的答案同时隐藏了两个按钮,这就是我感到困惑的原因。
以下内容似乎提供了解决方案(尽管使用 CSS 隐藏似乎是最可行的方法,特别是如果 FK 的“添加另一个”按钮在内联表单中):
A
有一个指向 B
的外键,所以一个 A
对象只能有 一个 对 B
的引用,并且他正在引用 A 表单中 B 字段旁边的 **+**。基本上,如果您参考另一个 问题 中的图像,OP 想要删除 #1 红色圆圈中的加号,而您的答案则删除了 #3 或 #4。 - Ad Nclass ... #(Your inline class)
def has_add_permission(self, request):
return False
类似地,如果您想禁用“删除?”选项,请在Inline类中添加以下方法。
def has_delete_permission(self, request, obj=None):
return False
InlineModelAdmin
的子类(而不是ModelAdmin
),需要显式地添加到其他ModelAdmin
的inlines
中。让我修复has_add_permission
,它需要obj
参数(def has_add_permission(self, request, obj):
)。 - okapies注意:适用于DJango 1.5.2版本以及可能更旧的版本。 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
在这里进行更改的有趣部分是,get_form
中的 author.widget
是 django.contrib.admin.widgets.RelatedFieldWidgetWrapper
的实例,而如果您尝试在其中一个 formfield_for_xxxxx
函数中进行更改,则小部件是实际表单小部件的实例,在这种典型的 ForeignKey 情况下,它是一个 django.forms.widgets.Select
。
InlineModelAdmin
吗? - Ad N我使用以下方式处理 Form 和 InlineForm
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
内联表单
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
formfield_for_dbfield
的扩展中实现解决方案的位置,但没有提供明确的示例。formfield_for_dbfield
?它的文档字符串表明它是用于处理表单字段的指定钩子:
它还允许(稍微)更清晰、更简洁的代码,并且我们可以轻松设置其他表单指定给定数据库字段实例的表单Field实例的钩子。
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.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中提及的事情?Django现在已经可以实现这个功能了。
你考虑过使用CSS来隐藏按钮吗?也许这有点hacky。
我没有测试过,但我想...
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文档--作为静态定义的媒体
注意/编辑: 文档中说文件将以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',) }
your_python_installation\Lib\site-packages\django\contrib\admin\templates\admin\submit_line.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
正如评论中所指出的:
max_num = 0
这里也已经确认过了: https://code.djangoproject.com/ticket/13424#comment:1
附注:这对于内联也适用。
max_num=0
- andilabs