diff --git a/django/contrib/admin/static/admin/js/prepopulate.js b/django/contrib/admin/static/admin/js/prepopulate.js index 24f24f95a8..db7903a5ef 100644 --- a/django/contrib/admin/static/admin/js/prepopulate.js +++ b/django/contrib/admin/static/admin/js/prepopulate.js @@ -3,32 +3,37 @@ /* Depends on urlify.js Populates a selected field with the values of the dependent fields, - URLifies and shortens the string. - dependencies - array of dependent fields id's - maxLength - maximum length of the URLify'd string + URLifies and shortens the string. + dependencies - array of dependent fields ids + maxLength - maximum length of the URLify'd string */ return this.each(function() { - var field = $(this); - - field.data('_changed', false); - field.change(function() { - field.data('_changed', true); - }); + var prepopulatedField = $(this); var populate = function () { - // Bail if the fields value has changed - if (field.data('_changed') == true) return; - + // Bail if the field's value has been changed by the user + if (prepopulatedField.data('_changed')) { + return; + } + var values = []; $.each(dependencies, function(i, field) { - if ($(field).val().length > 0) { - values.push($(field).val()); - } - }) - field.val(URLify(values.join(' '), maxLength)); + field = $(field); + if (field.val().length > 0) { + values.push(field.val()); + } + }); + prepopulatedField.val(URLify(values.join(' '), maxLength)); }; - $(dependencies.join(',')).keyup(populate).change(populate).focus(populate); + prepopulatedField.data('_changed', false); + prepopulatedField.change(function() { + prepopulatedField.data('_changed', true); + }); + + if (!prepopulatedField.val()) { + $(dependencies.join(',')).keyup(populate).change(populate).focus(populate); + } }); }; })(django.jQuery); diff --git a/django/contrib/admin/static/admin/js/prepopulate.min.js b/django/contrib/admin/static/admin/js/prepopulate.min.js index aa94937963..4a75827c97 100644 --- a/django/contrib/admin/static/admin/js/prepopulate.min.js +++ b/django/contrib/admin/static/admin/js/prepopulate.min.js @@ -1 +1 @@ -(function(a){a.fn.prepopulate=function(d,g){return this.each(function(){var b=a(this);b.data("_changed",false);b.change(function(){b.data("_changed",true)});var c=function(){if(b.data("_changed")!=true){var e=[];a.each(d,function(h,f){a(f).val().length>0&&e.push(a(f).val())});b.val(URLify(e.join(" "),g))}};a(d.join(",")).keyup(c).change(c).focus(c)})}})(django.jQuery); +(function(b){b.fn.prepopulate=function(e,g){return this.each(function(){var a=b(this),d=function(){if(!a.data("_changed")){var f=[];b.each(e,function(h,c){c=b(c);c.val().length>0&&f.push(c.val())});a.val(URLify(f.join(" "),g))}};a.data("_changed",false);a.change(function(){a.data("_changed",true)});a.val()||b(e.join(",")).keyup(d).change(d).focus(d)})}})(django.jQuery); diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py index 98ac1a657e..005bde3023 100644 --- a/django/contrib/admin/templatetags/admin_modify.py +++ b/django/contrib/admin/templatetags/admin_modify.py @@ -9,7 +9,7 @@ def prepopulated_fields_js(context): the prepopulated fields for both the admin form and inlines. """ prepopulated_fields = [] - if context['add'] and 'adminform' in context: + if 'adminform' in context: prepopulated_fields.extend(context['adminform'].prepopulated_fields) if 'inline_admin_formsets' in context: for inline_admin_formset in context['inline_admin_formsets']: diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index 5ec4fbb544..a1131210d7 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -644,8 +644,8 @@ class MainPrepopulated(models.Model): max_length=20, choices=(('option one', 'Option One'), ('option two', 'Option Two'))) - slug1 = models.SlugField() - slug2 = models.SlugField() + slug1 = models.SlugField(blank=True) + slug2 = models.SlugField(blank=True) class RelatedPrepopulated(models.Model): parent = models.ForeignKey(MainPrepopulated) diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 19c5207cf9..fdb5d7d436 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -3438,6 +3438,50 @@ class SeleniumAdminViewsFirefoxTests(AdminSeleniumWebDriverTestCase): slug2='option-one-tabular-inline-ignored-characters', ) + def test_populate_existing_object(self): + """ + Ensure that the prepopulation works for existing objects too, as long + as the original field is empty. + Refs #19082. + """ + # Slugs are empty to start with. + item = MainPrepopulated.objects.create( + name=' this is the mAin nÀMë', + pubdate='2012-02-18', + status='option two', + slug1='', + slug2='', + ) + self.admin_login(username='super', + password='secret', + login_url='/test_admin/admin/') + + object_url = '%s%s' % ( + self.live_server_url, + '/test_admin/admin/admin_views/mainprepopulated/{}/'.format(item.id)) + + self.selenium.get(object_url) + self.selenium.find_element_by_css_selector('#id_name').send_keys(' the best') + + # The slugs got prepopulated since they were originally empty + slug1 = self.selenium.find_element_by_css_selector('#id_slug1').get_attribute('value') + slug2 = self.selenium.find_element_by_css_selector('#id_slug2').get_attribute('value') + self.assertEqual(slug1, 'main-name-best-2012-02-18') + self.assertEqual(slug2, 'option-two-main-name-best') + + # Save the object + self.selenium.find_element_by_xpath('//input[@value="Save"]').click() + self.wait_page_loaded() + + self.selenium.get(object_url) + self.selenium.find_element_by_css_selector('#id_name').send_keys(' hello') + + # The slugs got prepopulated didn't change since they were originally not empty + slug1 = self.selenium.find_element_by_css_selector('#id_slug1').get_attribute('value') + slug2 = self.selenium.find_element_by_css_selector('#id_slug2').get_attribute('value') + self.assertEqual(slug1, 'main-name-best-2012-02-18') + self.assertEqual(slug2, 'option-two-main-name-best') + def test_collapsible_fieldset(self): """ Test that the 'collapse' class in fieldsets definition allows to