Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 603
guettli Avatar asked Oct 14 '21 07:10

guettli


2 Answers

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

like image 155
JPG Avatar answered Sep 19 '22 01:09

JPG


There are several ways to go. Here is one.

Add some javascript that changes the existing link behavior. Add the following script at the end of the overridden admin template admin/widgets/related_widget_wrapper.html. It removes the class which triggers the modal and changes the link to the object.

It will only be triggered for id_company field. Change to your needs.

{% block javascript %}
<script>
'use strict';
{
    const $ = django.jQuery;

    function changeEditButton() {
        const edit_btn = document.getElementById('change_id_company');
        const value = edit_btn.previousElementSibling.value;
        const split_link_template = edit_btn.getAttribute('data-href-template').split('?');
        edit_btn.classList.remove('related-widget-wrapper-link');
        edit_btn.setAttribute('href', split_link_template[0].replace('__fk__', value));
    };

    $(document).ready(function() {
        changeEditButton();
        $('body').on('change', '#id_company', function(e) {
            changeEditButton();
        });
    });
}
</script>
{% endblock %}

This code can also be modified to be triggered for all edit buttons and not only for the company edit button.

like image 25
Marco Avatar answered Sep 21 '22 01:09

Marco