mirror of https://github.com/django/django.git
Fixed #30064 -- Added form to validate admin search fields query input.
This commit is contained in:
parent
6b16c91157
commit
5b4c6b58a0
|
@ -1,6 +1,8 @@
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib import messages
|
||||||
from django.contrib.admin import FieldListFilter
|
from django.contrib.admin import FieldListFilter
|
||||||
from django.contrib.admin.exceptions import (
|
from django.contrib.admin.exceptions import (
|
||||||
DisallowedModelAdminLookup, DisallowedModelAdminToField,
|
DisallowedModelAdminLookup, DisallowedModelAdminToField,
|
||||||
|
@ -34,7 +36,18 @@ IGNORED_PARAMS = (
|
||||||
ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR, TO_FIELD_VAR)
|
ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR, TO_FIELD_VAR)
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeListSearchForm(forms.Form):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
# Populate "fields" dynamically because SEARCH_VAR is a variable:
|
||||||
|
self.fields = {
|
||||||
|
SEARCH_VAR: forms.CharField(required=False, strip=False),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ChangeList:
|
class ChangeList:
|
||||||
|
search_form_class = ChangeListSearchForm
|
||||||
|
|
||||||
def __init__(self, request, model, list_display, list_display_links,
|
def __init__(self, request, model, list_display, list_display_links,
|
||||||
list_filter, date_hierarchy, search_fields, list_select_related,
|
list_filter, date_hierarchy, search_fields, list_select_related,
|
||||||
list_per_page, list_max_show_all, list_editable, model_admin, sortable_by):
|
list_per_page, list_max_show_all, list_editable, model_admin, sortable_by):
|
||||||
|
@ -56,6 +69,11 @@ class ChangeList:
|
||||||
self.sortable_by = sortable_by
|
self.sortable_by = sortable_by
|
||||||
|
|
||||||
# Get search parameters from the query string.
|
# Get search parameters from the query string.
|
||||||
|
_search_form = self.search_form_class(request.GET)
|
||||||
|
if not _search_form.is_valid():
|
||||||
|
for error in _search_form.errors.values():
|
||||||
|
messages.error(request, ', '.join(error))
|
||||||
|
self.query = _search_form.cleaned_data.get(SEARCH_VAR) or ''
|
||||||
try:
|
try:
|
||||||
self.page_num = int(request.GET.get(PAGE_VAR, 0))
|
self.page_num = int(request.GET.get(PAGE_VAR, 0))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -76,7 +94,6 @@ class ChangeList:
|
||||||
self.list_editable = ()
|
self.list_editable = ()
|
||||||
else:
|
else:
|
||||||
self.list_editable = list_editable
|
self.list_editable = list_editable
|
||||||
self.query = request.GET.get(SEARCH_VAR, '')
|
|
||||||
self.queryset = self.get_queryset(request)
|
self.queryset = self.get_queryset(request)
|
||||||
self.get_results(request)
|
self.get_results(request)
|
||||||
if self.is_popup:
|
if self.is_popup:
|
||||||
|
|
|
@ -8,6 +8,7 @@ from django.contrib.admin.tests import AdminSeleniumTestCase
|
||||||
from django.contrib.admin.views.main import ALL_VAR, SEARCH_VAR
|
from django.contrib.admin.views.main import ALL_VAR, SEARCH_VAR
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.contrib.messages.storage.cookie import CookieStorage
|
||||||
from django.db import connection, models
|
from django.db import connection, models
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from django.db.models.fields import Field, IntegerField
|
from django.db.models.fields import Field, IntegerField
|
||||||
|
@ -406,6 +407,22 @@ class ChangeListTests(TestCase):
|
||||||
# Make sure distinct() was called
|
# Make sure distinct() was called
|
||||||
self.assertEqual(cl.queryset.count(), 1)
|
self.assertEqual(cl.queryset.count(), 1)
|
||||||
|
|
||||||
|
def test_changelist_search_form_validation(self):
|
||||||
|
m = ConcertAdmin(Concert, custom_site)
|
||||||
|
tests = [
|
||||||
|
({SEARCH_VAR: '\x00'}, 'Null characters are not allowed.'),
|
||||||
|
({SEARCH_VAR: 'some\x00thing'}, 'Null characters are not allowed.'),
|
||||||
|
]
|
||||||
|
for case, error in tests:
|
||||||
|
with self.subTest(case=case):
|
||||||
|
request = self.factory.get('/concert/', case)
|
||||||
|
request.user = self.superuser
|
||||||
|
request._messages = CookieStorage(request)
|
||||||
|
m.get_changelist_instance(request)
|
||||||
|
messages = [m.message for m in request._messages]
|
||||||
|
self.assertEqual(1, len(messages))
|
||||||
|
self.assertEqual(error, messages[0])
|
||||||
|
|
||||||
def test_distinct_for_non_unique_related_object_in_search_fields(self):
|
def test_distinct_for_non_unique_related_object_in_search_fields(self):
|
||||||
"""
|
"""
|
||||||
Regressions tests for #15819: If a field listed in search_fields
|
Regressions tests for #15819: If a field listed in search_fields
|
||||||
|
|
Loading…
Reference in New Issue