diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 2557de9cd2..dab27d5437 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -38,7 +38,7 @@ from django.template.response import SimpleTemplateResponse, TemplateResponse from django.utils import six from django.utils.decorators import method_decorator from django.utils.encoding import force_text, python_2_unicode_compatible -from django.utils.html import escape, escapejs +from django.utils.html import escape from django.utils.http import urlencode from django.utils.safestring import mark_safe from django.utils.text import capfirst, get_text_list @@ -1113,9 +1113,9 @@ class ModelAdmin(BaseModelAdmin): new_value = obj.serializable_value(attr) return SimpleTemplateResponse('admin/popup_response.html', { 'action': 'change', - 'value': escape(value), - 'obj': escapejs(obj), - 'new_value': escape(new_value), + 'value': value, + 'obj': obj, + 'new_value': new_value, }) opts = self.model._meta diff --git a/docs/releases/1.9.2.txt b/docs/releases/1.9.2.txt index 375fe3edaa..ffe5030d9e 100644 --- a/docs/releases/1.9.2.txt +++ b/docs/releases/1.9.2.txt @@ -21,3 +21,7 @@ Bugfixes * Fixed a regression that caused the incorrect day to be selected when opening the admin calendar widget for timezones from GMT+0100 to GMT+1200 (:ticket:`24980`). + +* Fixed a regression in the admin's edit related model popup that caused an + escaped value to be displayed in the select dropdown of the parent window + (:ticket:`25997`). diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index 90603ce828..e6dacd4aa8 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -34,16 +34,16 @@ from .models import ( GenRelReference, Grommet, ImplicitlyGeneratedPK, Ingredient, InlineReference, InlineReferer, Inquisition, Language, Link, MainPrepopulated, ModelWithStringPrimaryKey, NotReferenced, OldSubscriber, - OtherStory, Paper, Parent, ParentWithDependentChildren, Person, Persona, - Picture, Pizza, Plot, PlotDetails, PluggableSearchPerson, Podcast, Post, - PrePopulatedPost, PrePopulatedPostLargeSlug, PrePopulatedSubPost, Promo, - Question, Recipe, Recommendation, Recommender, ReferencedByGenRel, - ReferencedByInline, ReferencedByParent, RelatedPrepopulated, Report, - Reservation, Restaurant, RowLevelChangePermissionModel, Section, - ShortMessage, Simple, Sketch, State, Story, StumpJoke, Subscriber, - SuperVillain, Telegram, Thing, Topping, UnchangeableObject, - UndeletableObject, UnorderedObject, UserMessenger, Villain, Vodcast, - Whatsit, Widget, Worker, WorkHour, + OtherStory, Paper, Parent, ParentWithDependentChildren, ParentWithUUIDPK, + Person, Persona, Picture, Pizza, Plot, PlotDetails, PluggableSearchPerson, + Podcast, Post, PrePopulatedPost, PrePopulatedPostLargeSlug, + PrePopulatedSubPost, Promo, Question, Recipe, Recommendation, Recommender, + ReferencedByGenRel, ReferencedByInline, ReferencedByParent, + RelatedPrepopulated, RelatedWithUUIDPKModel, Report, Reservation, + Restaurant, RowLevelChangePermissionModel, Section, ShortMessage, Simple, + Sketch, State, Story, StumpJoke, Subscriber, SuperVillain, Telegram, Thing, + Topping, UnchangeableObject, UndeletableObject, UnorderedObject, + UserMessenger, Villain, Vodcast, Whatsit, Widget, Worker, WorkHour, ) @@ -950,6 +950,8 @@ site.register(ReferencedByInline) site.register(InlineReferer, InlineRefererAdmin) site.register(ReferencedByGenRel) site.register(GenRelReference) +site.register(ParentWithUUIDPK) +site.register(RelatedWithUUIDPKModel) # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. # That way we cover all four cases: diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index c105c42e4a..04a1fcbc93 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import datetime import os import tempfile +import uuid from django.contrib.auth.models import User from django.contrib.contenttypes.fields import ( @@ -952,3 +953,15 @@ class ReferencedByGenRel(models.Model): class GenRelReference(models.Model): references = GenericRelation(ReferencedByGenRel) + + +class ParentWithUUIDPK(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + title = models.CharField(max_length=100) + + def __str__(self): + return str(self.id) + + +class RelatedWithUUIDPKModel(models.Model): + parent = models.ForeignKey(ParentWithUUIDPK, on_delete=models.CASCADE) diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index f0780ea615..32dd1fba9c 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -51,14 +51,15 @@ from .models import ( ExternalSubscriber, Fabric, FancyDoodad, FieldOverridePost, FilteredManager, FooAccount, FoodDelivery, FunkyTag, Gallery, Grommet, Inquisition, Language, Link, MainPrepopulated, ModelWithStringPrimaryKey, - OtherStory, Paper, Parent, ParentWithDependentChildren, Person, Persona, - Picture, Pizza, Plot, PlotDetails, PluggableSearchPerson, Podcast, Post, - PrePopulatedPost, Promo, Question, Recommendation, Recommender, - RelatedPrepopulated, Report, Restaurant, RowLevelChangePermissionModel, - SecretHideout, Section, ShortMessage, Simple, State, Story, Subscriber, - SuperSecretHideout, SuperVillain, Telegram, TitleTranslation, Topping, - UnchangeableObject, UndeletableObject, UnorderedObject, Villain, Vodcast, - Whatsit, Widget, Worker, WorkHour, + OtherStory, Paper, Parent, ParentWithDependentChildren, ParentWithUUIDPK, + Person, Persona, Picture, Pizza, Plot, PlotDetails, PluggableSearchPerson, + Podcast, Post, PrePopulatedPost, Promo, Question, Recommendation, + Recommender, RelatedPrepopulated, RelatedWithUUIDPKModel, Report, + Restaurant, RowLevelChangePermissionModel, SecretHideout, Section, + ShortMessage, Simple, State, Story, Subscriber, SuperSecretHideout, + SuperVillain, Telegram, TitleTranslation, Topping, UnchangeableObject, + UndeletableObject, UnorderedObject, Villain, Vodcast, Whatsit, Widget, + Worker, WorkHour, ) @@ -4569,6 +4570,22 @@ class SeleniumAdminViewsFirefoxTests(AdminSeleniumWebDriverTestCase): self.selenium.close() self.selenium.switch_to.window(self.selenium.window_handles[0]) + def test_inline_uuid_pk_edit_with_popup(self): + from selenium.webdriver.support.ui import Select + parent = ParentWithUUIDPK.objects.create(title='test') + related_with_parent = RelatedWithUUIDPKModel.objects.create(parent=parent) + self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) + change_url = reverse('admin:admin_views_relatedwithuuidpkmodel_change', args=(related_with_parent.id,)) + self.selenium.get(self.live_server_url + change_url) + self.selenium.find_element_by_id('change_id_parent').click() + self.wait_for_popup() + self.selenium.switch_to.window(self.selenium.window_handles[-1]) + self.selenium.find_element_by_xpath('//input[@value="Save"]').click() + self.selenium.switch_to.window(self.selenium.window_handles[0]) + select = Select(self.selenium.find_element_by_id('id_parent')) + self.assertEqual(select.first_selected_option.text, str(parent.id)) + self.assertEqual(select.first_selected_option.get_attribute('value'), str(parent.id)) + class SeleniumAdminViewsChromeTests(SeleniumAdminViewsFirefoxTests): webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'