Link 在 django admin 中到外键对象
Link in django admin to foreign key object
我有一个带有指向模型 B 的外键的模型 A。
在 Django 管理中,如何在打开模型 B 管理页面的 ForeignKey 字段旁边的模型 A 管理页面中添加 link?
您可以执行以下操作:
models.py(示例):
model B(models.Model):
name = models.CharField(max_length=20)
model A(models.Model):
field1 = models.CharField(max_length=20)
Bkey = models.ForeignKey(B)
admin.py
from django.core import urlresolvers
class AAdmin(admin.ModelAdmin):
list_display = ["field1","link_to_B"]
def link_to_B(self, obj):
link=urlresolvers.reverse("admin:yourapp_b_change", args=[obj.B.id]) #model name has to be lowercase
return u'<a href="%s">%s</a>' % (link,obj.B.name)
link_to_B.allow_tags=True
将 yourapp 替换为您的应用名称。
除了已接受的答案外,在较新版本的 Django 中,reverse 方法现在位于 django.urls[=26= 包中](参见this link)。
另外,你应该在后台使用format_html函数输出HTML。那么allow_tags就没用了
最后,要在用户的编辑页面添加一个link,我在admin.py
中有这个功能:
from django.urls import reverse
from django.utils.html import format_html
class ObjectAdmin(admin.ModelAdmin):
list_display = ('name', 'link_to_user')
def link_to_user(self, obj):
link = reverse("admin:auth_user_change", args=[obj.user_id])
return format_html('<a href="{}">Edit {}</a>', link, obj.user.username)
link_to_user.short_description = 'Edit user'
不要忘记查看评论,有一些注意事项需要考虑。
Django 2.0+ 和 Python 3.5+:
from django.urls import reverse
from django.utils.html import escape, mark_safe
@admin.register(models.YourModel)
class YourModelAdmin(BaseModelAdmin):
def model_str(self, obj: models.YourModel):
link = reverse("admin:module_model_change", args=[obj.model_id])
return mark_safe(f'<a href="{link}">{escape(obj.model.__str__())}</a>')
model_str.short_description = 'Model'
model_str.admin_order_field = 'model' # Make row sortable
list_display = (
'model_str',
)
今天有一个更简单的解决方案,related
是要链接到的外键字段:
class YourModelAdmin(model.modelAdmin):
list_display = ["field_one", "field_two", "related"]
list_display_links = ["field_one", "related"]
我创建了 mixin,它对多对多关系做了类似的事情(在那里它显示了相关对象的计数和使用适当过滤器的更改列表的链接)。基于我从中分叉的要点:
https://gist.github.com/hovi/2e3a216ecc4be685ec9e0d23b0eb7901
在 django 1.1.x 和 1.0.x
上测试
如果你需要
- 在详细信息页面(不在列表页面)
上有一个指向 FK 页面的 href link
- 通过阻止加载 choices/options 到 FK 小部件来加快加载时间
Code for Django 3.2 for classes FKLinkWidget and CustomModelAdmin
[下面的代码在 Django 1.8 上测试 Python 3]
第 1 步:定义基本管理助手
class FKLinkWidget(forms.TextInput):
"""Widget to show html link for FK field instead of default option field"""
NO_VALUE_TEXT = 'None'
def __init__(self, attrs=None):
self.app_label = None
self.model_name = None
self.pk = None
self.repr = None
super().__init__(attrs)
def set_obj(self, obj):
self.app_label = obj._meta.app_label
self.model_name = obj._meta.model_name
self.pk = obj.pk
self.repr = str(obj)
def render(self, name, value, attrs=None):
if self.pk:
view_name = f"admin:{self.app_label}_{self.model_name}_change"
link_url = reverse(view_name, args=[self.pk])
return format_html('<a href="{}" target="_blank">{}</a>', link_url, self.repr)
else:
return format_html(value or self.NO_VALUE_TEXT)
class CustomModelAdmin(admin.ModelAdmin):
"""extendable ModelAdmin which provides several custom attributes
- fk_links = list of FK fields that should be shown as read-only links on detail page
this can prevent loading all choice options by django admin, which results 504 http error
"""
fk_links = []
def __init__(self, model, admin_site):
super().__init__(model, admin_site)
intersect = set(self.fk_links).intersection(self.readonly_fields + self.raw_id_fields)
if intersect:
raise ValueError(f'CustomModelAdmin fields: {intersect} are in readonly or raw_id fields')
def get_form(self, request, obj=None, **kwargs):
self.obj = obj
form = super().get_form(request, obj, **kwargs)
return form
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name in self.fk_links:
kwargs['widget'] = FKLinkWidget
formfield = super().formfield_for_dbfield(db_field, **kwargs)
if db_field.name in self.fk_links:
# we disable any actions for that field
if self.obj:
fk = getattr(self.obj, db_field.name)
if fk:
formfield.widget.widget.set_obj(fk)
formfield.widget.can_add_related = False
formfield.widget.can_change_related = False
formfield.widget.can_delete_related = False
return formfield
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name in self.fk_links:
kwargs["queryset"] = db_field.rel.to._default_manager.none()
kwargs["required"] = False
return super().formfield_for_foreignkey(db_field, request, **kwargs)
第 2 步:使用 CustomModelAdmin 作为管理模型的基础 class
@admin.register(UserProfile)
class UserProfileAdmin(CustomModelAdmin):
fk_links = ['user',]
第 3 步:结果看起来像
我有一个带有指向模型 B 的外键的模型 A。 在 Django 管理中,如何在打开模型 B 管理页面的 ForeignKey 字段旁边的模型 A 管理页面中添加 link?
您可以执行以下操作:
models.py(示例):
model B(models.Model):
name = models.CharField(max_length=20)
model A(models.Model):
field1 = models.CharField(max_length=20)
Bkey = models.ForeignKey(B)
admin.py
from django.core import urlresolvers
class AAdmin(admin.ModelAdmin):
list_display = ["field1","link_to_B"]
def link_to_B(self, obj):
link=urlresolvers.reverse("admin:yourapp_b_change", args=[obj.B.id]) #model name has to be lowercase
return u'<a href="%s">%s</a>' % (link,obj.B.name)
link_to_B.allow_tags=True
将 yourapp 替换为您的应用名称。
除了已接受的答案外,在较新版本的 Django 中,reverse 方法现在位于 django.urls[=26= 包中](参见this link)。
另外,你应该在后台使用format_html函数输出HTML。那么allow_tags就没用了
最后,要在用户的编辑页面添加一个link,我在admin.py
中有这个功能:
from django.urls import reverse
from django.utils.html import format_html
class ObjectAdmin(admin.ModelAdmin):
list_display = ('name', 'link_to_user')
def link_to_user(self, obj):
link = reverse("admin:auth_user_change", args=[obj.user_id])
return format_html('<a href="{}">Edit {}</a>', link, obj.user.username)
link_to_user.short_description = 'Edit user'
不要忘记查看评论,有一些注意事项需要考虑。
Django 2.0+ 和 Python 3.5+:
from django.urls import reverse
from django.utils.html import escape, mark_safe
@admin.register(models.YourModel)
class YourModelAdmin(BaseModelAdmin):
def model_str(self, obj: models.YourModel):
link = reverse("admin:module_model_change", args=[obj.model_id])
return mark_safe(f'<a href="{link}">{escape(obj.model.__str__())}</a>')
model_str.short_description = 'Model'
model_str.admin_order_field = 'model' # Make row sortable
list_display = (
'model_str',
)
今天有一个更简单的解决方案,related
是要链接到的外键字段:
class YourModelAdmin(model.modelAdmin):
list_display = ["field_one", "field_two", "related"]
list_display_links = ["field_one", "related"]
我创建了 mixin,它对多对多关系做了类似的事情(在那里它显示了相关对象的计数和使用适当过滤器的更改列表的链接)。基于我从中分叉的要点:
https://gist.github.com/hovi/2e3a216ecc4be685ec9e0d23b0eb7901
在 django 1.1.x 和 1.0.x
上测试如果你需要
- 在详细信息页面(不在列表页面) 上有一个指向 FK 页面的 href link
- 通过阻止加载 choices/options 到 FK 小部件来加快加载时间
Code for Django 3.2 for classes FKLinkWidget and CustomModelAdmin
[下面的代码在 Django 1.8 上测试 Python 3]
第 1 步:定义基本管理助手
class FKLinkWidget(forms.TextInput):
"""Widget to show html link for FK field instead of default option field"""
NO_VALUE_TEXT = 'None'
def __init__(self, attrs=None):
self.app_label = None
self.model_name = None
self.pk = None
self.repr = None
super().__init__(attrs)
def set_obj(self, obj):
self.app_label = obj._meta.app_label
self.model_name = obj._meta.model_name
self.pk = obj.pk
self.repr = str(obj)
def render(self, name, value, attrs=None):
if self.pk:
view_name = f"admin:{self.app_label}_{self.model_name}_change"
link_url = reverse(view_name, args=[self.pk])
return format_html('<a href="{}" target="_blank">{}</a>', link_url, self.repr)
else:
return format_html(value or self.NO_VALUE_TEXT)
class CustomModelAdmin(admin.ModelAdmin):
"""extendable ModelAdmin which provides several custom attributes
- fk_links = list of FK fields that should be shown as read-only links on detail page
this can prevent loading all choice options by django admin, which results 504 http error
"""
fk_links = []
def __init__(self, model, admin_site):
super().__init__(model, admin_site)
intersect = set(self.fk_links).intersection(self.readonly_fields + self.raw_id_fields)
if intersect:
raise ValueError(f'CustomModelAdmin fields: {intersect} are in readonly or raw_id fields')
def get_form(self, request, obj=None, **kwargs):
self.obj = obj
form = super().get_form(request, obj, **kwargs)
return form
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name in self.fk_links:
kwargs['widget'] = FKLinkWidget
formfield = super().formfield_for_dbfield(db_field, **kwargs)
if db_field.name in self.fk_links:
# we disable any actions for that field
if self.obj:
fk = getattr(self.obj, db_field.name)
if fk:
formfield.widget.widget.set_obj(fk)
formfield.widget.can_add_related = False
formfield.widget.can_change_related = False
formfield.widget.can_delete_related = False
return formfield
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name in self.fk_links:
kwargs["queryset"] = db_field.rel.to._default_manager.none()
kwargs["required"] = False
return super().formfield_for_foreignkey(db_field, request, **kwargs)
第 2 步:使用 CustomModelAdmin 作为管理模型的基础 class
@admin.register(UserProfile)
class UserProfileAdmin(CustomModelAdmin):
fk_links = ['user',]
第 3 步:结果看起来像