52
You can do the following:
models.py (example):
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
Replace yourapp with the name of your app.
56
In addition of the accepted answer, in newer versions of Django, the reverse method is now in the package django.urls (cf. this link).
Moreover, you should use the format_html function to output HTML in the admin. Then the allow_tags becomes useless.
Finally, to add a link to the edit page of a user, I have this function in 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'
Donβt forget to check the comments, there are a few considerations to take into account.
- [Django]-Django-admin.py makemessages not working
- [Django]-Managing static files for multiple apps in Django
- [Django]-What is the difference between static files and media files in Django?
24
Django 2.0+ and 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',
)
- [Django]-Django: Use of DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT in settings.py?
- [Django]-RuntimeError: Model class django.contrib.sites.models.Site doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS
- [Django]-Temporarily Disabling Django Caching
4
If you need to
- have a href link to FK page on detail page (not on the list page)
- speed up loading time by preventing loading choices/options to FK widget
Code for Django 3.2 for classes FKLinkWidget and CustomModelAdmin
[code below tested on Django 1.8 with Python 3]
Step 1: define base admin helpers
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)
Step 2: use CustomModelAdmin as base class for your admin model
@admin.register(UserProfile)
class UserProfileAdmin(CustomModelAdmin):
fk_links = ['user',]
Step 3: result it will looks like
- [Django]-Django-allauth social account connect to existing account on login
- [Django]-Django 1.3.1 compilemessages. Error: sh: msgfmt: command not found
- [Django]-Django migrate βfake and βfake-initial explained
1
There is an easier solution today, with related
being the foreign key field to be linked to:
class YourModelAdmin(model.modelAdmin):
list_display = ["field_one", "field_two", "related"]
list_display_links = ["field_one", "related"]
- [Django]-Different db for testing in Django?
- [Django]-Why don't my south migrations work?
- [Django]-Explicitly set MySQL table storage engine using South and Django
1
I created mixin, that does this + similar thing to many-to-many relations (there it shows count of related objects and links to changelist with apropriate filter). Based on gist Iβve forked from:
https://gist.github.com/hovi/2e3a216ecc4be685ec9e0d23b0eb7901
Tested on django 1.1.x and 1.0.x
- [Django]-Django: import auth user to the model
- [Django]-Deploying Django with gunicorn and nginx
- [Django]-Django-Bower + Foundation 5 + SASS, How to configure?