diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
index a85045c..0c63b99 100644
a
|
b
|
from __future__ import unicode_literals
|
2 | 2 | |
3 | 3 | import datetime |
4 | 4 | import decimal |
| 5 | import re |
5 | 6 | |
6 | 7 | from django.db import models |
7 | 8 | from django.db.models.constants import LOOKUP_SEP |
… |
… |
def prepare_lookup_value(key, value):
|
36 | 37 | """ |
37 | 38 | # if key ends with __in, split parameter into separate values |
38 | 39 | if key.endswith('__in'): |
39 | | value = value.split(',') |
| 40 | # Use '\,' to escape ',', and '\\' to escape '\' |
| 41 | value = [re.sub(r"\\([\\,])", r"\1", v) for v in re.split(r'(?:(?<!\\)|(?<=\\\\)),', value)] |
40 | 42 | # if key ends with __isnull, special case '' and false |
41 | 43 | if key.endswith('__isnull'): |
42 | 44 | if value.lower() in ('', 'false'): |
diff --git a/tests/regressiontests/admin_filters/tests.py b/tests/regressiontests/admin_filters/tests.py
index 11f792e..6f7d88b 100644
a
|
b
|
class ListFiltersTests(TestCase):
|
163 | 163 | self.dev = Department.objects.create(code='DEV', description='Development') |
164 | 164 | self.design = Department.objects.create(code='DSN', description='Design') |
165 | 165 | |
166 | | # Employees |
167 | | self.john = Employee.objects.create(name='John Blue', department=self.dev) |
168 | | self.jack = Employee.objects.create(name='Jack Red', department=self.design) |
| 166 | # Employees, with special characters in names. Refs #19721 |
| 167 | self.john = Employee.objects.create(name='Red, John', department=self.dev) |
| 168 | self.jack = Employee.objects.create(name='Blue, Jack \\', department=self.design) |
169 | 169 | |
170 | 170 | def get_changelist(self, request, model, modeladmin): |
171 | 171 | return ChangeList(request, model, modeladmin.list_display, modeladmin.list_display_links, |
… |
… |
class ListFiltersTests(TestCase):
|
741 | 741 | self.assertEqual(choices[2]['display'], 'Design') |
742 | 742 | self.assertEqual(choices[2]['selected'], False) |
743 | 743 | self.assertEqual(choices[2]['query_string'], '?department__code__exact=DSN') |
| 744 | |
| 745 | def test_comma_in___in_data(self): |
| 746 | """ |
| 747 | Ensure that we can use values with ',' and '\\' with __in, by escaping |
| 748 | Refs #19721 |
| 749 | """ |
| 750 | modeladmin = EmployeeAdmin(Employee, site) |
| 751 | |
| 752 | employees = [self.jack, self.john] |
| 753 | |
| 754 | def quote(name): |
| 755 | return name.replace("\\", "\\\\").replace(",", "\\,") |
| 756 | |
| 757 | request = self.request_factory.get('/', {'name__in': ",".join(quote(e.name) for e in employees)}) |
| 758 | |
| 759 | changelist = self.get_changelist(request, Employee, modeladmin) |
| 760 | |
| 761 | # Make sure the correct queryset is returned |
| 762 | queryset = changelist.get_query_set(request) |
| 763 | self.assertEqual(list(queryset), employees) |