Skip to content

Commit

Permalink
[2.2.x] Fixed #31031 -- Fixed data loss in admin changelist view when…
Browse files Browse the repository at this point in the history
… formset's prefix contains regex special chars.

Regression in b18650a.

Backport of 52936ef from master
  • Loading branch information
bmispelon authored and felixxm committed Nov 26, 2019
1 parent 2af6060 commit 7873d37
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 2 deletions.
4 changes: 3 additions & 1 deletion django/contrib/admin/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -1638,7 +1638,9 @@ def change_view(self, request, object_id, form_url='', extra_context=None):

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):
Expand Down
4 changes: 3 additions & 1 deletion docs/releases/2.2.8.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 20 additions & 0 deletions tests/admin_changelist/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,26 @@ def test_get_list_editable_queryset(self):
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)
Expand Down

0 comments on commit 7873d37

Please sign in to comment.