瀏覽代碼

Fixed #31031 -- Fixed data loss in admin changelist view when formset's prefix contains regex special chars.

Regression in b18650a2634890aa758abae2f33875daa13a9ba3.
Baptiste Mispelon 5 年之前
父節點
當前提交
52936eface
共有 3 個文件被更改,包括 26 次插入2 次删除
  1. 3 1
      django/contrib/admin/options.py
  2. 3 1
      docs/releases/2.2.8.txt
  3. 20 0
      tests/admin_changelist/tests.py

+ 3 - 1
django/contrib/admin/options.py

@@ -1631,7 +1631,9 @@ class ModelAdmin(BaseModelAdmin):
 
     def _get_edited_object_pks(self, request, prefix):
         """Return POST data values of list_editable primary keys."""
-        pk_pattern = re.compile(r'{}-\d+-{}$'.format(prefix, self.model._meta.pk.name))
+        pk_pattern = re.compile(
+            r'{}-\d+-{}$'.format(re.escape(prefix), self.model._meta.pk.name)
+        )
         return [value for key, value in request.POST.items() if pk_pattern.match(key)]
 
     def _get_list_editable_queryset(self, request, prefix):

+ 3 - 1
docs/releases/2.2.8.txt

@@ -10,4 +10,6 @@ Django 2.2.8 fixes several bugs in 2.2.7 and adds compatibility with Python
 Bugfixes
 ========
 
-* ...
+* Fixed a data loss possibility in the admin changelist view when a custom
+  :ref:`formset's prefix <formset-prefix>` contains regular expression special
+  characters, e.g. `'$'` (:ticket:`31031`).

+ 20 - 0
tests/admin_changelist/tests.py

@@ -844,6 +844,26 @@ class ChangeListTests(TestCase):
         queryset = m._get_list_editable_queryset(request, prefix='form')
         self.assertEqual(queryset.count(), 2)
 
+    def test_get_list_editable_queryset_with_regex_chars_in_prefix(self):
+        a = Swallow.objects.create(origin='Swallow A', load=4, speed=1)
+        Swallow.objects.create(origin='Swallow B', load=2, speed=2)
+        data = {
+            'form$-TOTAL_FORMS': '2',
+            'form$-INITIAL_FORMS': '2',
+            'form$-MIN_NUM_FORMS': '0',
+            'form$-MAX_NUM_FORMS': '1000',
+            'form$-0-uuid': str(a.pk),
+            'form$-0-load': '10',
+            '_save': 'Save',
+        }
+        superuser = self._create_superuser('superuser')
+        self.client.force_login(superuser)
+        changelist_url = reverse('admin:admin_changelist_swallow_changelist')
+        m = SwallowAdmin(Swallow, custom_site)
+        request = self.factory.post(changelist_url, data=data)
+        queryset = m._get_list_editable_queryset(request, prefix='form$')
+        self.assertEqual(queryset.count(), 1)
+
     def test_changelist_view_list_editable_changed_objects_uses_filter(self):
         """list_editable edits use a filtered queryset to limit memory usage."""
         a = Swallow.objects.create(origin='Swallow A', load=4, speed=1)