瀏覽代碼

Fixed #11157 -- Stopped removing stop words in admin's prepopulated_fields.

Co-Authored-By: Andy Chosak <andy@chosak.org>
Scott Cranfill 4 年之前
父節點
當前提交
62f1655a64

+ 2 - 0
AUTHORS

@@ -73,6 +73,7 @@ answer newbie questions, and generally made Django that much better:
     Andrew Pinkham <http://AndrewsForge.com>
     Andrew Pinkham <http://AndrewsForge.com>
     Andrews Medina <andrewsmedina@gmail.com>
     Andrews Medina <andrewsmedina@gmail.com>
     Andriy Sokolovskiy <me@asokolovskiy.com>
     Andriy Sokolovskiy <me@asokolovskiy.com>
+    Andy Chosak <andy@chosak.org>
     Andy Dustman <farcepest@gmail.com>
     Andy Dustman <farcepest@gmail.com>
     Andy Gayton <andy-django@thecablelounge.com>
     Andy Gayton <andy-django@thecablelounge.com>
     andy@jadedplanet.net
     andy@jadedplanet.net
@@ -803,6 +804,7 @@ answer newbie questions, and generally made Django that much better:
     schwank@gmail.com
     schwank@gmail.com
     Scot Hacker <shacker@birdhouse.org>
     Scot Hacker <shacker@birdhouse.org>
     Scott Barr <scott@divisionbyzero.com.au>
     Scott Barr <scott@divisionbyzero.com.au>
+    Scott Cranfill <scott@scottcranfill.com>
     Scott Fitsimones <scott@airgara.ge>
     Scott Fitsimones <scott@airgara.ge>
     Scott Pashley <github@scottpashley.co.uk>
     Scott Pashley <github@scottpashley.co.uk>
     scott@staplefish.com
     scott@staplefish.com

+ 0 - 14
django/contrib/admin/static/admin/js/urlify.js

@@ -148,23 +148,9 @@
 
 
     function URLify(s, num_chars, allowUnicode) {
     function URLify(s, num_chars, allowUnicode) {
         // changes, e.g., "Petty theft" to "petty-theft"
         // changes, e.g., "Petty theft" to "petty-theft"
-        // remove all these words from the string before urlifying
         if (!allowUnicode) {
         if (!allowUnicode) {
             s = downcode(s);
             s = downcode(s);
         }
         }
-        const hasUnicodeChars = /[^\u0000-\u007f]/.test(s);
-        // Remove English words only if the string contains ASCII (English)
-        // characters.
-        if (!hasUnicodeChars) {
-            const removeList = [
-                "a", "an", "as", "at", "before", "but", "by", "for", "from",
-                "is", "in", "into", "like", "of", "off", "on", "onto", "per",
-                "since", "than", "the", "this", "that", "to", "up", "via",
-                "with"
-            ];
-            const r = new RegExp('\\b(' + removeList.join('|') + ')\\b', 'gi');
-            s = s.replace(r, '');
-        }
         s = s.toLowerCase(); // convert to lowercase
         s = s.toLowerCase(); // convert to lowercase
         // if downcode doesn't hit, the char will be stripped here
         // if downcode doesn't hit, the char will be stripped here
         if (allowUnicode) {
         if (allowUnicode) {

+ 6 - 2
docs/ref/contrib/admin/index.txt

@@ -1096,8 +1096,7 @@ subclass::
     automatically generate the value for ``SlugField`` fields from one or more
     automatically generate the value for ``SlugField`` fields from one or more
     other fields. The generated value is produced by concatenating the values
     other fields. The generated value is produced by concatenating the values
     of the source fields, and then by transforming that result into a valid
     of the source fields, and then by transforming that result into a valid
-    slug (e.g. substituting dashes for spaces; lowercasing ASCII letters; and
-    removing various English stop words such as 'a', 'an', 'as', and similar).
+    slug (e.g. substituting dashes for spaces and lowercasing ASCII letters).
 
 
     Prepopulated fields aren't modified by JavaScript after a value has been
     Prepopulated fields aren't modified by JavaScript after a value has been
     saved. It's usually undesired that slugs change (which would cause an
     saved. It's usually undesired that slugs change (which would cause an
@@ -1106,6 +1105,11 @@ subclass::
     ``prepopulated_fields`` doesn't accept ``DateTimeField``, ``ForeignKey``,
     ``prepopulated_fields`` doesn't accept ``DateTimeField``, ``ForeignKey``,
     ``OneToOneField``, and ``ManyToManyField`` fields.
     ``OneToOneField``, and ``ManyToManyField`` fields.
 
 
+    .. versionchanged:: 3.2
+
+        In older versions, various English stop words are removed from
+        generated values.
+
 .. attribute:: ModelAdmin.preserve_filters
 .. attribute:: ModelAdmin.preserve_filters
 
 
     The admin now preserves filters on the list view after creating, editing
     The admin now preserves filters on the list view after creating, editing

+ 3 - 0
docs/releases/3.2.txt

@@ -275,6 +275,9 @@ Miscellaneous
   external build tool. The minified vendored JavaScript files packaged with the
   external build tool. The minified vendored JavaScript files packaged with the
   admin (e.g. :ref:`jquery.min.js <contrib-admin-jquery>`) are still included.
   admin (e.g. :ref:`jquery.min.js <contrib-admin-jquery>`) are still included.
 
 
+* :attr:`.ModelAdmin.prepopulated_fields` no longer strips English stop words,
+  such as ``'a'`` or ``'an'``.
+
 .. _deprecated-features-3.2:
 .. _deprecated-features-3.2:
 
 
 Features deprecated in 3.2
 Features deprecated in 3.2

+ 3 - 4
js_tests/admin/URLify.test.js

@@ -7,8 +7,8 @@ QUnit.test('empty string', function(assert) {
     assert.strictEqual(URLify('', 8, true), '');
     assert.strictEqual(URLify('', 8, true), '');
 });
 });
 
 
-QUnit.test('strip nonessential words', function(assert) {
-    assert.strictEqual(URLify('the D is silent', 8, true), 'd-silent');
+QUnit.test('preserve nonessential words', function(assert) {
+    assert.strictEqual(URLify('the D is silent', 15, true), 'the-d-is-silent');
 });
 });
 
 
 QUnit.test('strip non-URL characters', function(assert) {
 QUnit.test('strip non-URL characters', function(assert) {
@@ -23,7 +23,6 @@ QUnit.test('trim trailing hyphens', function(assert) {
     assert.strictEqual(URLify('D silent always', 9, true), 'd-silent');
     assert.strictEqual(URLify('D silent always', 9, true), 'd-silent');
 });
 });
 
 
-QUnit.test('do not remove English words if the string contains non-ASCII', function(assert) {
-    // If removing English words wasn't skipped, the last 'a' would be removed.
+QUnit.test('non-ASCII string', function(assert) {
     assert.strictEqual(URLify('Kaupa-miða', 255, true), 'kaupa-miða');
     assert.strictEqual(URLify('Kaupa-miða', 255, true), 'kaupa-miða');
 });
 });

+ 30 - 30
tests/admin_views/tests.py

@@ -4453,13 +4453,13 @@ class SeleniumTests(AdminSeleniumTestCase):
         # Main form ----------------------------------------------------------
         # Main form ----------------------------------------------------------
         self.selenium.find_element_by_id('id_pubdate').send_keys('2012-02-18')
         self.selenium.find_element_by_id('id_pubdate').send_keys('2012-02-18')
         self.select_option('#id_status', 'option two')
         self.select_option('#id_status', 'option two')
-        self.selenium.find_element_by_id('id_name').send_keys(' this is the mAin nÀMë and it\'s awεšomeıııİ')
+        self.selenium.find_element_by_id('id_name').send_keys(' the mAin nÀMë and it\'s awεšomeıııİ')
         slug1 = self.selenium.find_element_by_id('id_slug1').get_attribute('value')
         slug1 = self.selenium.find_element_by_id('id_slug1').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_slug2').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_slug2').get_attribute('value')
         slug3 = self.selenium.find_element_by_id('id_slug3').get_attribute('value')
         slug3 = self.selenium.find_element_by_id('id_slug3').get_attribute('value')
-        self.assertEqual(slug1, 'main-name-and-its-awesomeiiii-2012-02-18')
-        self.assertEqual(slug2, 'option-two-main-name-and-its-awesomeiiii')
-        self.assertEqual(slug3, 'this-is-the-main-n\xe0m\xeb-and-its-aw\u03b5\u0161ome\u0131\u0131\u0131i')
+        self.assertEqual(slug1, 'the-main-name-and-its-awesomeiiii-2012-02-18')
+        self.assertEqual(slug2, 'option-two-the-main-name-and-its-awesomeiiii')
+        self.assertEqual(slug3, 'the-main-n\xe0m\xeb-and-its-aw\u03b5\u0161ome\u0131\u0131\u0131i')
 
 
         # Stacked inlines ----------------------------------------------------
         # Stacked inlines ----------------------------------------------------
         # Initial inline
         # Initial inline
@@ -4470,8 +4470,8 @@ class SeleniumTests(AdminSeleniumTestCase):
         )
         )
         slug1 = self.selenium.find_element_by_id('id_relatedprepopulated_set-0-slug1').get_attribute('value')
         slug1 = self.selenium.find_element_by_id('id_relatedprepopulated_set-0-slug1').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_relatedprepopulated_set-0-slug2').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_relatedprepopulated_set-0-slug2').get_attribute('value')
-        self.assertEqual(slug1, 'here-stacked-inline-2011-12-17')
-        self.assertEqual(slug2, 'option-one-here-stacked-inline')
+        self.assertEqual(slug1, 'here-is-a-stacked-inline-2011-12-17')
+        self.assertEqual(slug2, 'option-one-here-is-a-stacked-inline')
         initial_select2_inputs = self.selenium.find_elements_by_class_name('select2-selection')
         initial_select2_inputs = self.selenium.find_elements_by_class_name('select2-selection')
         # Inline formsets have empty/invisible forms.
         # Inline formsets have empty/invisible forms.
         # Only the 4 visible select2 inputs are initialized.
         # Only the 4 visible select2 inputs are initialized.
@@ -4493,9 +4493,9 @@ class SeleniumTests(AdminSeleniumTestCase):
         slug1 = self.selenium.find_element_by_id('id_relatedprepopulated_set-1-slug1').get_attribute('value')
         slug1 = self.selenium.find_element_by_id('id_relatedprepopulated_set-1-slug1').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_relatedprepopulated_set-1-slug2').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_relatedprepopulated_set-1-slug2').get_attribute('value')
         # 50 characters maximum for slug1 field
         # 50 characters maximum for slug1 field
-        self.assertEqual(slug1, 'now-you-have-another-stacked-inline-very-loooooooo')
+        self.assertEqual(slug1, 'now-you-have-another-stacked-inline-with-a-very-lo')
         # 60 characters maximum for slug2 field
         # 60 characters maximum for slug2 field
-        self.assertEqual(slug2, 'option-two-now-you-have-another-stacked-inline-very-looooooo')
+        self.assertEqual(slug2, 'option-two-now-you-have-another-stacked-inline-with-a-very-l')
 
 
         # Tabular inlines ----------------------------------------------------
         # Tabular inlines ----------------------------------------------------
         # Initial inline
         # Initial inline
@@ -4506,8 +4506,8 @@ class SeleniumTests(AdminSeleniumTestCase):
         )
         )
         slug1 = self.selenium.find_element_by_id('id_relatedprepopulated_set-2-0-slug1').get_attribute('value')
         slug1 = self.selenium.find_element_by_id('id_relatedprepopulated_set-2-0-slug1').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_relatedprepopulated_set-2-0-slug2').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_relatedprepopulated_set-2-0-slug2').get_attribute('value')
-        self.assertEqual(slug1, 'and-now-tabular-inline-1234-12-07')
-        self.assertEqual(slug2, 'option-two-and-now-tabular-inline')
+        self.assertEqual(slug1, 'and-now-with-a-tabular-inline-1234-12-07')
+        self.assertEqual(slug2, 'option-two-and-now-with-a-tabular-inline')
 
 
         # Add an inline
         # Add an inline
         # Button may be outside the browser frame.
         # Button may be outside the browser frame.
@@ -4521,12 +4521,12 @@ class SeleniumTests(AdminSeleniumTestCase):
         self.selenium.find_element_by_id('id_relatedprepopulated_set-2-1-pubdate').send_keys('1981-08-22')
         self.selenium.find_element_by_id('id_relatedprepopulated_set-2-1-pubdate').send_keys('1981-08-22')
         self.select_option('#id_relatedprepopulated_set-2-1-status', 'option one')
         self.select_option('#id_relatedprepopulated_set-2-1-status', 'option one')
         self.selenium.find_element_by_id('id_relatedprepopulated_set-2-1-name').send_keys(
         self.selenium.find_element_by_id('id_relatedprepopulated_set-2-1-name').send_keys(
-            r'a tÃbűlaŘ inline with ignored ;"&*^\%$#@-/`~ characters'
+            r'tÃbűlaŘ inline with ignored ;"&*^\%$#@-/`~ characters'
         )
         )
         slug1 = self.selenium.find_element_by_id('id_relatedprepopulated_set-2-1-slug1').get_attribute('value')
         slug1 = self.selenium.find_element_by_id('id_relatedprepopulated_set-2-1-slug1').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_relatedprepopulated_set-2-1-slug2').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_relatedprepopulated_set-2-1-slug2').get_attribute('value')
-        self.assertEqual(slug1, 'tabular-inline-ignored-characters-1981-08-22')
-        self.assertEqual(slug2, 'option-one-tabular-inline-ignored-characters')
+        self.assertEqual(slug1, 'tabular-inline-with-ignored-characters-1981-08-22')
+        self.assertEqual(slug2, 'option-one-tabular-inline-with-ignored-characters')
         # Add an inline without an initial inline.
         # Add an inline without an initial inline.
         # The button is outside of the browser frame.
         # The button is outside of the browser frame.
         self.selenium.execute_script("window.scrollTo(0, document.body.scrollHeight);")
         self.selenium.execute_script("window.scrollTo(0, document.body.scrollHeight);")
@@ -4540,42 +4540,42 @@ class SeleniumTests(AdminSeleniumTestCase):
             self.selenium.find_element_by_xpath('//input[@value="Save"]').click()
             self.selenium.find_element_by_xpath('//input[@value="Save"]').click()
         self.assertEqual(MainPrepopulated.objects.all().count(), 1)
         self.assertEqual(MainPrepopulated.objects.all().count(), 1)
         MainPrepopulated.objects.get(
         MainPrepopulated.objects.get(
-            name=' this is the mAin nÀMë and it\'s awεšomeıııİ',
+            name=' the mAin nÀMë and it\'s awεšomeıııİ',
             pubdate='2012-02-18',
             pubdate='2012-02-18',
             status='option two',
             status='option two',
-            slug1='main-name-and-its-awesomeiiii-2012-02-18',
-            slug2='option-two-main-name-and-its-awesomeiiii',
-            slug3='this-is-the-main-nàmë-and-its-awεšomeıııi',
+            slug1='the-main-name-and-its-awesomeiiii-2012-02-18',
+            slug2='option-two-the-main-name-and-its-awesomeiiii',
+            slug3='the-main-nàmë-and-its-awεšomeıııi',
         )
         )
         self.assertEqual(RelatedPrepopulated.objects.all().count(), 4)
         self.assertEqual(RelatedPrepopulated.objects.all().count(), 4)
         RelatedPrepopulated.objects.get(
         RelatedPrepopulated.objects.get(
             name=' here is a sŤāÇkeð   inline !  ',
             name=' here is a sŤāÇkeð   inline !  ',
             pubdate='2011-12-17',
             pubdate='2011-12-17',
             status='option one',
             status='option one',
-            slug1='here-stacked-inline-2011-12-17',
-            slug2='option-one-here-stacked-inline',
+            slug1='here-is-a-stacked-inline-2011-12-17',
+            slug2='option-one-here-is-a-stacked-inline',
         )
         )
         RelatedPrepopulated.objects.get(
         RelatedPrepopulated.objects.get(
             # 75 characters in name field
             # 75 characters in name field
             name=' now you haVe anöther   sŤāÇkeð  inline with a very ... loooooooooooooooooo',
             name=' now you haVe anöther   sŤāÇkeð  inline with a very ... loooooooooooooooooo',
             pubdate='1999-01-25',
             pubdate='1999-01-25',
             status='option two',
             status='option two',
-            slug1='now-you-have-another-stacked-inline-very-loooooooo',
-            slug2='option-two-now-you-have-another-stacked-inline-very-looooooo',
+            slug1='now-you-have-another-stacked-inline-with-a-very-lo',
+            slug2='option-two-now-you-have-another-stacked-inline-with-a-very-l',
         )
         )
         RelatedPrepopulated.objects.get(
         RelatedPrepopulated.objects.get(
             name='And now, with a tÃbűlaŘ inline !!!',
             name='And now, with a tÃbűlaŘ inline !!!',
             pubdate='1234-12-07',
             pubdate='1234-12-07',
             status='option two',
             status='option two',
-            slug1='and-now-tabular-inline-1234-12-07',
-            slug2='option-two-and-now-tabular-inline',
+            slug1='and-now-with-a-tabular-inline-1234-12-07',
+            slug2='option-two-and-now-with-a-tabular-inline',
         )
         )
         RelatedPrepopulated.objects.get(
         RelatedPrepopulated.objects.get(
-            name=r'a tÃbűlaŘ inline with ignored ;"&*^\%$#@-/`~ characters',
+            name=r'tÃbűlaŘ inline with ignored ;"&*^\%$#@-/`~ characters',
             pubdate='1981-08-22',
             pubdate='1981-08-22',
             status='option one',
             status='option one',
-            slug1='tabular-inline-ignored-characters-1981-08-22',
-            slug2='option-one-tabular-inline-ignored-characters',
+            slug1='tabular-inline-with-ignored-characters-1981-08-22',
+            slug2='option-one-tabular-inline-with-ignored-characters',
         )
         )
 
 
     def test_populate_existing_object(self):
     def test_populate_existing_object(self):
@@ -4601,8 +4601,8 @@ class SeleniumTests(AdminSeleniumTestCase):
         # The slugs got prepopulated since they were originally empty
         # The slugs got prepopulated since they were originally empty
         slug1 = self.selenium.find_element_by_id('id_slug1').get_attribute('value')
         slug1 = self.selenium.find_element_by_id('id_slug1').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_slug2').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_slug2').get_attribute('value')
-        self.assertEqual(slug1, 'main-name-best-2012-02-18')
-        self.assertEqual(slug2, 'option-two-main-name-best')
+        self.assertEqual(slug1, 'this-is-the-main-name-the-best-2012-02-18')
+        self.assertEqual(slug2, 'option-two-this-is-the-main-name-the-best')
 
 
         # Save the object
         # Save the object
         with self.wait_page_loaded():
         with self.wait_page_loaded():
@@ -4614,8 +4614,8 @@ class SeleniumTests(AdminSeleniumTestCase):
         # The slugs got prepopulated didn't change since they were originally not empty
         # The slugs got prepopulated didn't change since they were originally not empty
         slug1 = self.selenium.find_element_by_id('id_slug1').get_attribute('value')
         slug1 = self.selenium.find_element_by_id('id_slug1').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_slug2').get_attribute('value')
         slug2 = self.selenium.find_element_by_id('id_slug2').get_attribute('value')
-        self.assertEqual(slug1, 'main-name-best-2012-02-18')
-        self.assertEqual(slug2, 'option-two-main-name-best')
+        self.assertEqual(slug1, 'this-is-the-main-name-the-best-2012-02-18')
+        self.assertEqual(slug2, 'option-two-this-is-the-main-name-the-best')
 
 
     def test_collapsible_fieldset(self):
     def test_collapsible_fieldset(self):
         """
         """