[英]Disable link to edit object in django's admin (display list only)?
In Django's admin, I want disable the links provided on the "select item to change" page so that users cannot go anywhere to edit the item.在 Django 的管理员中,我想禁用“选择要更改的项目”页面上提供的链接,以便用户无法在任何地方编辑项目 go。 (I am going to limit what the users can do with this list to a set of drop down actions - no actual editing of fields).
(我打算将用户可以使用此列表执行的操作限制为一组下拉操作 - 没有实际编辑字段)。
I see that Django has the ability to choose which fields display the link , however, I can't see how I can have none of them.我看到 Django 有能力选择哪些字段显示链接,但是,我看不出我怎么能没有它们。
class HitAdmin(admin.ModelAdmin):
list_display = ('user','ip','user_agent','hitcount')
search_fields = ('ip','user_agent')
date_hierarchy = 'created'
list_display_links = [] # doesn't work, goes to default
Any ideas how to get my object list without any links to edit?任何想法如何在没有任何编辑链接的情况下获取我的 object 列表?
I wanted a Log viewer as a list only.我只想要一个日志查看器作为列表。
I got it working like this:我让它像这样工作:
class LogEntryAdmin(ModelAdmin):
actions = None
list_display = (
'action_time', 'user',
'content_type', 'object_repr',
'change_message')
search_fields = ['=user__username', ]
fieldsets = [
(None, {'fields':()}),
]
def __init__(self, *args, **kwargs):
super(LogEntryAdmin, self).__init__(*args, **kwargs)
self.list_display_links = (None, )
It is kind of a mix between both answers.这是两种答案的混合。
If you just do self.list_display_links = ()
it will show the link, Anyway because the template-tag
code (templatetags/admin_list.py) checks again to see if the list is empty.如果你只是做
self.list_display_links = ()
它会显示链接,因为template-tag
代码 (templatetags/admin_list.py) 再次检查列表是否为空。
Doing this properly requires two steps:正确执行此操作需要两个步骤:
The second part is important: if you don't do this then people will still be able to access the change view by entering a URL directly (which presumably you don't want).第二部分很重要:如果您不这样做,那么人们仍然可以通过直接输入 URL 来访问更改视图(这可能是您不想要的)。 This is closely related to what OWASP term an "Insecure Direct Object Reference" .
这与 OWASP 术语“不安全的直接对象引用”密切相关。
As part of this answer I'll build a ReadOnlyMixin
class that can be used to provide all the functionality shown.作为此答案的一部分,我将构建一个
ReadOnlyMixin
类,该类可用于提供显示的所有功能。
Django 1.7 makes this really easy: you just set list_display_links
to None
. Django 1.7 使这变得非常简单:您只需将
list_display_links
设置为None
。
class ReadOnlyMixin(): # Add inheritance from "object" if using Python 2
list_display_links = None
Django 1.6 (and presumably earlier) don't make this so simple. Django 1.6(大概更早版本)并没有让这变得如此简单。 Quite a lot of answers to this question have suggested overriding
__init__
in order to set list_display_links
after the object has been constructed, but this makes it harder to reuse (we can only override the constructor once).这个问题的很多答案都建议覆盖
__init__
以在构造对象后设置list_display_links
,但这使得重用变得更加困难(我们只能覆盖一次构造函数)。
I think a better option is to override Django's get_list_display_links
method as follows:我认为更好的选择是覆盖 Django 的
get_list_display_links
方法,如下所示:
def get_list_display_links(self, request, list_display):
"""
Return a sequence containing the fields to be displayed as links
on the changelist. The list_display parameter is the list of fields
returned by get_list_display().
We override Django's default implementation to specify no links unless
these are explicitly set.
"""
if self.list_display_links or not list_display:
return self.list_display_links
else:
return (None,)
This makes our mixin easy to use: it hides the edit link by default but allows us to add it back in if required for a particular admin view.这使我们的 mixin 易于使用:它默认隐藏编辑链接,但允许我们在特定管理视图需要时将其添加回来。
We can change the behaviour of the detail page (change view) by overriding the change_view
method.我们可以通过覆盖
change_view
方法来更改详细信息页面的行为(更改视图)。 Here's an extension to the technique suggested by Chris Pratt which automatically finds the right page:这是 Chris Pratt 建议的技术的扩展,它会自动找到正确的页面:
enable_change_view = False
def change_view(self, request, object_id, form_url='', extra_context=None):
"""
The 'change' admin view for this model.
We override this to redirect back to the changelist unless the view is
specifically enabled by the "enable_change_view" property.
"""
if self.enable_change_view:
return super(ReportMixin, self).change_view(
request,
object_id,
form_url,
extra_context
)
else:
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
opts = self.model._meta
url = reverse('admin:{app}_{model}_changelist'.format(
app=opts.app_label,
model=opts.model_name,
))
return HttpResponseRedirect(url)
Again this is customisable - by toggling enable_change_view
to True
you can switch the details page back on.同样,这是可自定义的 - 通过将
enable_change_view
切换为True
您可以重新打开详细信息页面。
Finally, you might want to override the following methods in order to prevent people adding or deleting new items.最后,您可能希望覆盖以下方法以防止人们添加或删除新项目。
def has_add_permission(self, request):
return False
def has_delete_permission(self, request, obj=None):
return False
These changes will:这些变化将:
/add
to the URL/add
到 URL 来直接添加项目Finally you can remove the "Delete selected items " action by modifying the actions
parameter.最后,您可以通过修改
actions
参数来删除“删除所选项目”操作。
Here's the completed mixin:这是完成的混合:
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
class ReadOnlyMixin(): # Add inheritance from "object" if using Python 2
actions = None
enable_change_view = False
def get_list_display_links(self, request, list_display):
"""
Return a sequence containing the fields to be displayed as links
on the changelist. The list_display parameter is the list of fields
returned by get_list_display().
We override Django's default implementation to specify no links unless
these are explicitly set.
"""
if self.list_display_links or not list_display:
return self.list_display_links
else:
return (None,)
def change_view(self, request, object_id, form_url='', extra_context=None):
"""
The 'change' admin view for this model.
We override this to redirect back to the changelist unless the view is
specifically enabled by the "enable_change_view" property.
"""
if self.enable_change_view:
return super(ReportMixin, self).change_view(
request,
object_id,
form_url,
extra_context
)
else:
opts = self.model._meta
url = reverse('admin:{app}_{model}_changelist'.format(
app=opts.app_label,
model=opts.model_name,
))
return HttpResponseRedirect(url)
def has_add_permission(self, request):
return False
def has_delete_permission(self, request, obj=None):
return False
In Django 1.7 and later, you can do在 Django 1.7 及更高版本中,你可以这样做
class HitAdmin(admin.ModelAdmin):
list_display_links = None
As user, omat, mentioned in a comment above, any attempt to simply remove the links does not prevent users from still accessing the change page manually.作为用户 omat,在上面的评论中提到,任何简单地删除链接的尝试都不会阻止用户仍然手动访问更改页面。 However, that, too, is easy enough to remedy:
然而,这也很容易补救:
class MyModelAdmin(admin.ModelAdmin)
# Other stuff here
def change_view(self, request, obj=None):
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist'))
In your model admin set:在您的模型管理集中:
list_display_links = (None,)
That should do it.那应该这样做。 (Works in 1.1.1 anyway.)
(无论如何都可以在 1.1.1 中使用。)
Link to docs: list_display_links链接到文档: list_display_links
只需在您的管理员中写入list_display_links = None
Just for the notes, you may modify changelist_view:只是为了笔记,你可以修改changelist_view:
class SomeAdmin(admin.ModelAdmin):
def changelist_view(self, request, extra_context=None):
self.list_display_links = (None, )
return super(SomeAdmin, self).changelist_view(request, extra_context=None)
This works fine for me.这对我来说很好用。
In more "recent" versions of Django, since at least 1.9, it is possible to simple determine the add, change and delete permissions on the admin class.在 Django 的“最新”版本中,至少从 1.9 开始,可以简单地确定管理类的添加、更改和删除权限。 See the django admin documentation for reference.
请参阅django 管理文档以供参考。 Here is an example:
下面是一个例子:
@admin.register(Object)
class Admin(admin.ModelAdmin):
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
There isn't a supported way to do this.没有支持的方法来做到这一点。
Looking at the code, it seems that it automatically sets ModelAdmin.list_display_links
to the first element if you don't set it to anything.查看代码,如果您不将其设置为任何内容,它似乎会自动将
ModelAdmin.list_display_links
设置为第一个元素。 So the easiest way might be to override the __init__
method in your ModelAdmin
subclass to unset that attribute on initialization:因此,最简单的方法可能是覆盖
ModelAdmin
子类中的__init__
方法以在初始化时取消设置该属性:
class HitAdmin(admin.ModelAdmin):
list_display = ('user','ip','user_agent','hitcount')
search_fields = ('ip','user_agent')
date_hierarchy = 'created'
def __init__(self, *args, **kwargs):
super(HitAdmin, self).__init__(*args, **kwargs)
self.list_display_links = []
This appears to work, after a very cursory test.经过非常粗略的测试,这似乎有效。 I can't guarantee that it won't break anything elsewhere, or that it won't be broken by future changes to Django, though.
不过,我不能保证它不会在其他地方破坏任何东西,或者它不会被未来对 Django 的更改所破坏。
Edit after comment :评论后编辑:
No need to patch the source, this would work:无需修补源,这会起作用:
def __init__(self, *args, **kwargs):
if self.list_display_links:
unset_list_display = True
else:
unset_list_display = False
super(HitAdmin, self).__init__(*args, **kwargs)
if unset_list_display:
self.list_display_links = []
But I highly doubt any patch would be accepted into Django, since this breaks something that the code explicitly does at the moment.但我非常怀疑 Django 是否会接受任何补丁,因为这破坏了代码目前明确所做的事情。
You could also be ridiculously hacky about it (if you didn't want to fuss with overriding init
) and provide a value for the first element that basically looks like this:您也可能对此感到可笑(如果您不想为覆盖
init
大惊小怪)并为第一个元素提供一个基本上如下所示的值:
</a>My non-linked value<a>
I know, I know, not very pretty, but perhaps less anxiety about breaking something elsewhere since all we're doing is changing markup.我知道,我知道,不是很漂亮,但也许不会那么担心破坏其他地方的东西,因为我们所做的只是改变标记。
Here's some sample code about how this works:下面是一些关于它是如何工作的示例代码:
class HitAdmin(admin.ModelAdmin):
list_display = ('user_no_link','ip','user_agent','hitcount')
def user_no_link(self, obj):
return u'</a>%s<a>' % obj
user_no_link.allow_tags = True
user_no_link.short_description = "user"
Side Note: You could also improve the readability of the output (since you don't want it to be a link) by returning return u'%s' % obj.get_full_name()
which might be kinda neat depending on your use case.旁注:您还可以通过返回
return u'%s' % obj.get_full_name()
来提高输出的可读性(因为您不希望它成为链接),这取决于您的用例,这可能有点整洁。
with django 1.6.2 you can do like this:使用 django 1.6.2 你可以这样做:
class MyAdmin(admin.ModelAdmin):
def get_list_display_links(self, request, list_display):
return []
it will hide all auto generated links.它将隐藏所有自动生成的链接。
I overrided get_list_display_links method and action to None.我将 get_list_display_links 方法和操作覆盖为无。
class ChangeLogAdmin(admin.ModelAdmin):
actions = None
list_display = ('asset', 'field', 'before_value', 'after_value', 'operator', 'made_at')
fieldsets = [
(None, {'fields': ()}),
]
def __init__(self, model, admin_site):
super().__init__(model, admin_site)
def get_list_display_links(self, request, list_display):
super().get_list_display_links(request, list_display)
return None
I build a mixin based on @simpleigh 's solution.我基于@simpleigh 的解决方案构建了一个 mixin。
class DeactivatableChangeViewAdminMixin:
"""
Mixin to be used in model admins to disable the detail page / change view.
"""
enable_change_view = True
def can_see_change_view(self, request) -> bool:
"""
This method determines if the change view is disabled or visible.
"""
return self.enable_change_view
def get_list_display_links(self, request, list_display):
"""
When we don't want to show the change view, there is no need for having a link to it
"""
if not self.can_see_change_view(request=request):
return None
return super().get_list_display_links(request, list_display)
def change_view(self, request, *args, **kwargs):
"""
The 'change' admin view for this model.
We override this to redirect back to the changelist unless the view is
specifically enabled by the "enable_change_view" property.
"""
if self.can_see_change_view(request=request):
return super().change_view(request, *args, **kwargs)
else:
opts = self.model._meta
url = reverse('admin:{app}_{model}_changelist'.format(
app=opts.app_label,
model=opts.model_name,
))
return HttpResponseRedirect(url)
The benefit is that you can reuse it and you can furthermore make it conditional好处是您可以重用它,还可以使其成为有条件的
Build for django 3.2.8.为 django 3.2.8 构建。
To be used like this for a static approach:像这样用于 static 方法:
class MyAdmin(DeactivatableChangeViewAdminMixin, admin.ModelAdmin):
enable_change_view = False
And like this for a non-static one:像这样的非静态的:
class MyAdmin(DeactivatableChangeViewAdminMixin, admin.ModelAdmin):
def can_see_change_view(self, request) -> bool:
return request.user.my_condition
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.