diff options
-rw-r--r-- | django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js | 4 | ||||
-rw-r--r-- | django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html | 10 | ||||
-rw-r--r-- | django/contrib/admin/widgets.py | 4 | ||||
-rw-r--r-- | docs/intro/_images/admin09.png | bin | 6719 -> 8120 bytes | |||
-rw-r--r-- | docs/releases/4.1.txt | 2 | ||||
-rw-r--r-- | tests/admin_widgets/tests.py | 13 |
6 files changed, 30 insertions, 3 deletions
diff --git a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js index 668c56a89d..284d44ad62 100644 --- a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js +++ b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js @@ -125,7 +125,7 @@ this.textContent = newRepr; this.value = newId; } - }); + }).trigger('change'); selects.next().find('.select2-selection__rendered').each(function() { // The element can have a clear button as a child. // Use the lastChild to modify only the displayed value. @@ -178,7 +178,7 @@ event.preventDefault(); opener.dismissRelatedLookupPopup(window, $(this).data("popup-opener")); }); - $('body').on('click', '.related-widget-wrapper-link', function(e) { + $('body').on('click', '.related-widget-wrapper-link[data-popup="yes"]', function(e) { e.preventDefault(); if (this.href) { const event = $.Event('django:show-related', {href: this.href}); diff --git a/django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html b/django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html index a97bec16e3..9f7e586003 100644 --- a/django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html +++ b/django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html @@ -7,12 +7,14 @@ {% if can_change_related %} <a class="related-widget-wrapper-link change-related" id="change_id_{{ name }}" data-href-template="{{ change_related_template_url }}?{{ url_params }}" + data-popup="yes" title="{% blocktranslate %}Change selected {{ model }}{% endblocktranslate %}"> <img src="{% static 'admin/img/icon-changelink.svg' %}" alt="{% translate 'Change' %}"> </a> {% endif %} {% if can_add_related %} <a class="related-widget-wrapper-link add-related" id="add_id_{{ name }}" + data-popup="yes" href="{{ add_related_url }}?{{ url_params }}" title="{% blocktranslate %}Add another {{ model }}{% endblocktranslate %}"> <img src="{% static 'admin/img/icon-addlink.svg' %}" alt="{% translate 'Add' %}"> @@ -21,10 +23,18 @@ {% if can_delete_related %} <a class="related-widget-wrapper-link delete-related" id="delete_id_{{ name }}" data-href-template="{{ delete_related_template_url }}?{{ url_params }}" + data-popup="yes" title="{% blocktranslate %}Delete selected {{ model }}{% endblocktranslate %}"> <img src="{% static 'admin/img/icon-deletelink.svg' %}" alt="{% translate 'Delete' %}"> </a> {% endif %} + {% if can_view_related %} + <a class="related-widget-wrapper-link view-related" id="view_id_{{ name }}" + data-href-template="{{ change_related_template_url }}?{{ view_related_url_params }}" + title="{% blocktranslate %}View selected {{ model }}{% endblocktranslate %}"> + <img src="{% static 'admin/img/icon-viewlink.svg' %}" alt="{% translate 'View' %}"> + </a> + {% endif %} {% endif %} {% endspaceless %} {% endblock %} diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index c64c5d14f3..a361968b27 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -300,10 +300,11 @@ class RelatedFieldWidgetWrapper(forms.Widget): rel_opts = self.rel.model._meta info = (rel_opts.app_label, rel_opts.model_name) self.widget.choices = self.choices + related_field_name = self.rel.get_related_field().name url_params = "&".join( "%s=%s" % param for param in [ - (TO_FIELD_VAR, self.rel.get_related_field().name), + (TO_FIELD_VAR, related_field_name), (IS_POPUP_VAR, 1), ] ) @@ -325,6 +326,7 @@ class RelatedFieldWidgetWrapper(forms.Widget): info, "delete", "__fk__" ) if self.can_view_related or self.can_change_related: + context["view_related_url_params"] = f"{TO_FIELD_VAR}={related_field_name}" context["change_related_template_url"] = self.get_related_url( info, "change", "__fk__" ) diff --git a/docs/intro/_images/admin09.png b/docs/intro/_images/admin09.png Binary files differindex bfaeb3b99f..7b649dc1d3 100644 --- a/docs/intro/_images/admin09.png +++ b/docs/intro/_images/admin09.png diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index 46d0cf3540..e3112ad0f5 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -61,6 +61,8 @@ Minor features * The admin :meth:`history view <django.contrib.admin.ModelAdmin.history_view>` is now paginated. +* Related widget wrappers now have a link to object's change form. + :mod:`django.contrib.admindocs` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index e291801cae..db724b4196 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -1675,6 +1675,7 @@ class AdminRawIdWidgetSeleniumTests(AdminWidgetSeleniumTestCase): class RelatedFieldWidgetSeleniumTests(AdminWidgetSeleniumTestCase): def test_ForeignKey_using_to_field(self): from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import Select self.admin_login(username="super", password="secret", login_url="/") self.selenium.get( @@ -1698,6 +1699,12 @@ class RelatedFieldWidgetSeleniumTests(AdminWidgetSeleniumTestCase): # The field now contains the new user self.selenium.find_element(By.CSS_SELECTOR, "#id_user option[value=newuser]") + self.selenium.find_element(By.ID, "view_id_user").click() + self.wait_for_value("#id_username", "newuser") + self.selenium.back() + + select = Select(self.selenium.find_element(By.ID, "id_user")) + select.select_by_value("newuser") # Click the Change User button to change it self.selenium.find_element(By.ID, "change_id_user").click() self.wait_for_and_switch_to_popup() @@ -1714,6 +1721,12 @@ class RelatedFieldWidgetSeleniumTests(AdminWidgetSeleniumTestCase): By.CSS_SELECTOR, "#id_user option[value=changednewuser]" ) + self.selenium.find_element(By.ID, "view_id_user").click() + self.wait_for_value("#id_username", "changednewuser") + self.selenium.back() + + select = Select(self.selenium.find_element(By.ID, "id_user")) + select.select_by_value("changednewuser") # Go ahead and submit the form to make sure it works self.selenium.find_element(By.CSS_SELECTOR, save_button_css_selector).click() self.wait_for_text( |