summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js4
-rw-r--r--django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html10
-rw-r--r--django/contrib/admin/widgets.py4
-rw-r--r--docs/intro/_images/admin09.pngbin6719 -> 8120 bytes
-rw-r--r--docs/releases/4.1.txt2
-rw-r--r--tests/admin_widgets/tests.py13
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
index bfaeb3b99f..7b649dc1d3 100644
--- a/docs/intro/_images/admin09.png
+++ b/docs/intro/_images/admin09.png
Binary files differ
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(