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

Regression in b18650a263.

Backport of 52936eface from master
This commit is contained in:
Baptiste Mispelon 2019-11-24 23:09:07 +01:00 committed by Mariusz Felisiak
parent 2af606003a
commit 7873d3757d
3 changed files with 26 additions and 2 deletions

View File

@ -1638,7 +1638,9 @@ class ModelAdmin(BaseModelAdmin):
def _get_edited_object_pks(self, request, prefix): def _get_edited_object_pks(self, request, prefix):
"""Return POST data values of list_editable primary keys.""" """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)] return [value for key, value in request.POST.items() if pk_pattern.match(key)]
def _get_list_editable_queryset(self, request, prefix): def _get_list_editable_queryset(self, request, prefix):

View File

@ -10,4 +10,6 @@ Django 2.2.8 fixes several bugs in 2.2.7 and adds compatibility with Python
Bugfixes 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`).

View File

@ -827,6 +827,26 @@ class ChangeListTests(TestCase):
queryset = m._get_list_editable_queryset(request, prefix='form') queryset = m._get_list_editable_queryset(request, prefix='form')
self.assertEqual(queryset.count(), 2) 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): def test_changelist_view_list_editable_changed_objects_uses_filter(self):
"""list_editable edits use a filtered queryset to limit memory usage.""" """list_editable edits use a filtered queryset to limit memory usage."""
a = Swallow.objects.create(origin='Swallow A', load=4, speed=1) a = Swallow.objects.create(origin='Swallow A', load=4, speed=1)