Search code examples
djangodjango-admin

Django Admin: Add Hyperlink to related model


I would like to add a hyperlink to the related model Training

django-admin-add-hyperlink-to-related-model

It would be nice to have declarative solution, since I want to use this at several places.

The "pencil" icon opens the related model in a popup window. That's not what I want. I want a plain hyperlink to the related model.

BTW, if you use "raw_id_fields", then the result is exactly what I was looking for: There is a hyperlink to the corresponding admin interface of this ForeignKey.


Solution

  • Update Jan 4, 2023

    From Django 4.1, this becomes a part of the official build (related PR).

    Related widget wrappers now have a link to object’s change form

    Result

    output


    Previous Answer

    The class named RelatedFieldWidgetWrapper is showing the icons on the Django Admin page and thus you need to override the same. So, create a custom class as below,

    from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
    
    
    class CustomRelatedFieldWidgetWrapper(RelatedFieldWidgetWrapper):
        template_name = 'admin/widgets/custom_related_widget_wrapper.html'
    
        @classmethod
        def create_from_root(cls, root_widget: RelatedFieldWidgetWrapper):
            # You don't need this method of you are using the MonkeyPatch method
            set_attr_fields = [
                "widget", "rel", "admin_site", "can_add_related", "can_change_related",
                "can_delete_related", "can_view_related"
            ]
            init_args = {field: getattr(root_widget, field) for field in set_attr_fields}
            return CustomRelatedFieldWidgetWrapper(**init_args)
    
        def get_context(self, name, value, attrs):
            context = super().get_context(name, value, attrs)
            rel_opts = self.rel.model._meta
            info = (rel_opts.app_label, rel_opts.model_name)
            context['list_related_url'] = self.get_related_url(info, 'changelist')
            return context

    See, the context variable list_related_url is the relative path that we need here. Now, create an HTML file to render the output,

    #File: any_registered_appname/templates/admin/widgets/custom_related_widget_wrapper.html
    
    {% extends "admin/widgets/related_widget_wrapper.html" %}
    {% block links %}
        {{ block.super }}
        <a href="{{list_related_url}}">- Link To Related Model -</a>
    {% endblock %}
    

    How to connect?

    Method-1 : Monkey Patch

    # admin.py
    
    # other imports
    from ..widgets import CustomRelatedFieldWidgetWrapper
    from django.contrib.admin import widgets
    
    widgets.RelatedFieldWidgetWrapper = CustomRelatedFieldWidgetWrapper # monket patch
    

    Method-2 : Override ModelAdmin

    # admin.py
    class AlbumAdmin(admin.ModelAdmin):
        hyperlink_fields = ["related_field_1"]
    
        def formfield_for_dbfield(self, db_field, request, **kwargs):
            formfield = super().formfield_for_dbfield(db_field, request, **kwargs)
            if db_field.name in self.hyperlink_fields:
                formfield.widget = CustomRelatedFieldWidgetWrapper.create_from_root(
                    formfield.widget
                )
            return formfield

    Result

    Result Screenshot