diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 0b941df0919..94e65486502 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -277,9 +277,10 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)): return "%s__icontains" % field_name use_distinct = False - if self.search_fields and search_term: + search_fields = self.get_search_fields(request) + if search_fields and search_term: orm_lookups = [construct_search(str(search_field)) - for search_field in self.search_fields] + for search_field in search_fields] for bit in search_term.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] @@ -767,6 +768,13 @@ class ModelAdmin(BaseModelAdmin): """ return self.list_filter + def get_search_fields(self, request): + """ + Returns a sequence containing the fields to be searched whenever + somebody submits a search query. + """ + return self.search_fields + def get_preserved_filters(self, request): """ Returns the preserved filters querystring. @@ -1243,6 +1251,7 @@ class ModelAdmin(BaseModelAdmin): list_display = self.get_list_display(request) list_display_links = self.get_list_display_links(request, list_display) list_filter = self.get_list_filter(request) + search_fields = self.get_search_fields(request) # Check actions to see if any are available on this changelist actions = self.get_actions(request) @@ -1254,9 +1263,9 @@ class ModelAdmin(BaseModelAdmin): try: cl = ChangeList(request, self.model, list_display, list_display_links, list_filter, self.date_hierarchy, - self.search_fields, self.list_select_related, - self.list_per_page, self.list_max_show_all, self.list_editable, - self) + search_fields, self.list_select_related, self.list_per_page, + self.list_max_show_all, self.list_editable, self) + except IncorrectLookupParameters: # Wacky lookup parameters were given, so redirect to the main # changelist page, without parameters, and pass an 'invalid=1' diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 946f9157976..40ec5143316 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -1233,6 +1233,14 @@ templates used by the :class:`ModelAdmin` views: to return the same kind of sequence type as for the :attr:`~ModelAdmin.list_filter` attribute. +.. method:: ModelAdmin.get_search_fields(self, request) + + .. versionadded:: 1.7 + + The ``get_search_fields`` method is given the ``HttpRequest`` and is expected + to return the same kind of sequence type as for the + :attr:`~ModelAdmin.search_fields` attribute. + .. method:: ModelAdmin.get_inline_instances(self, request, obj=None) .. versionadded:: 1.5 diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index c47a392dff1..d21cb454289 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -110,6 +110,9 @@ Minor features or ``None`` for the key and the custom label as the value. The default blank option ``"----------"`` will be omitted in this case. +* The admin's search fields can now be customized per-request thanks to the new + :meth:`django.contrib.admin.ModelAdmin.get_search_fields` method. + Backwards incompatible changes in 1.7 ===================================== diff --git a/tests/admin_changelist/admin.py b/tests/admin_changelist/admin.py index db836cdf7dc..a63f2b60cea 100644 --- a/tests/admin_changelist/admin.py +++ b/tests/admin_changelist/admin.py @@ -105,3 +105,10 @@ class DynamicListFilterChildAdmin(admin.ModelAdmin): my_list_filter.remove('parent') return my_list_filter +class DynamicSearchFieldsChildAdmin(admin.ModelAdmin): + search_fields = ('name',) + + def get_search_fields(self, request): + search_fields = super(DynamicSearchFieldsChildAdmin, self).get_search_fields(request) + search_fields += ('age',) + return search_fields diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py index a65de464907..cb1ac5039ff 100644 --- a/tests/admin_changelist/tests.py +++ b/tests/admin_changelist/tests.py @@ -18,7 +18,8 @@ from .admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin, GroupAdmin, ParentAdmin, DynamicListDisplayChildAdmin, DynamicListDisplayLinksChildAdmin, CustomPaginationAdmin, FilteredChildAdmin, CustomPaginator, site as custom_site, - SwallowAdmin, DynamicListFilterChildAdmin, InvitationAdmin) + SwallowAdmin, DynamicListFilterChildAdmin, InvitationAdmin, + DynamicSearchFieldsChildAdmin) from .models import (Event, Child, Parent, Genre, Band, Musician, Group, Quartet, Membership, ChordsMusician, ChordsBand, Invitation, Swallow, UnorderedObject, OrderedObject, CustomIdUser) @@ -588,6 +589,13 @@ class ChangeListTests(TestCase): response = m.changelist_view(request) self.assertEqual(response.context_data['cl'].list_filter, ('parent', 'name', 'age')) + def test_dynamic_search_fields(self): + child = self._create_superuser('child') + m = DynamicSearchFieldsChildAdmin(Child, admin.site) + request = self._mocked_authenticated_request('/child/', child) + response = m.changelist_view(request) + self.assertEqual(response.context_data['cl'].search_fields, ('name', 'age')) + def test_pagination_page_range(self): """ Regression tests for ticket #15653: ensure the number of pages