Loading AUTHORS +1 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,7 @@ answer newbie questions, and generally made Django that much better: Brian Beck <http://blog.brianbeck.com/> Shannon -jj Behrens <http://jjinux.blogspot.com/> Esdras Beleza <linux@esdrasbeleza.com> Božidar Benko <bbenko@gmail.com> Chris Bennett <chrisrbennett@yahoo.com> Danilo Bargen Shai Berger <shai@platonix.com> Loading django/contrib/admin/options.py +30 −1 Original line number Diff line number Diff line import copy import operator from functools import update_wrapper, partial from django import forms Loading @@ -9,7 +10,7 @@ from django.forms.models import (modelform_factory, modelformset_factory, from django.contrib.contenttypes.models import ContentType from django.contrib.admin import widgets, helpers from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_objects, model_format_dict, NestedObjects) model_format_dict, NestedObjects, lookup_needs_distinct) from django.contrib.admin import validation from django.contrib.admin.templatetags.admin_static import static from django.contrib import messages Loading Loading @@ -255,6 +256,34 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)): """ return self.prepopulated_fields def get_search_results(self, request, queryset, search_term): # Apply keyword searches. def construct_search(field_name): if field_name.startswith('^'): return "%s__istartswith" % field_name[1:] elif field_name.startswith('='): return "%s__iexact" % field_name[1:] elif field_name.startswith('@'): return "%s__search" % field_name[1:] else: return "%s__icontains" % field_name use_distinct = False if self.search_fields and search_term: orm_lookups = [construct_search(str(search_field)) for search_field in self.search_fields] for bit in search_term.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct def get_queryset(self, request): """ Returns a QuerySet of all model instances that can be edited by the Loading django/contrib/admin/views/main.py +5 −27 Original line number Diff line number Diff line import operator import sys import warnings from functools import reduce from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured from django.core.paginator import InvalidPage Loading Loading @@ -331,7 +329,7 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)): def get_queryset(self, request): # First, we collect all the declared list filters. (self.filter_specs, self.has_filters, remaining_lookup_params, use_distinct) = self.get_filters(request) filters_use_distinct) = self.get_filters(request) # Then, we let every list filter modify the queryset to its liking. qs = self.root_queryset Loading Loading @@ -378,31 +376,11 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)): ordering = self.get_ordering(request, qs) qs = qs.order_by(*ordering) # Apply keyword searches. def construct_search(field_name): if field_name.startswith('^'): return "%s__istartswith" % field_name[1:] elif field_name.startswith('='): return "%s__iexact" % field_name[1:] elif field_name.startswith('@'): return "%s__search" % field_name[1:] else: return "%s__icontains" % field_name if self.search_fields and self.query: orm_lookups = [construct_search(str(search_field)) for search_field in self.search_fields] for bit in self.query.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] qs = qs.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.lookup_opts, search_spec): use_distinct = True break # Apply search results qs, search_use_distinct = self.model_admin.get_search_results(request, qs, self.query) if use_distinct: # Remove duplicates from results, if neccesary if filters_use_distinct | search_use_distinct: return qs.distinct() else: return qs Loading docs/ref/contrib/admin/index.txt +36 −0 Original line number Diff line number Diff line Loading @@ -1005,6 +1005,9 @@ subclass:: Performs a full-text match. This is like the default search method but uses an index. Currently this is only available for MySQL. If you need to customize search you can use :meth:`ModelAdmin.get_search_results` to provide additional or alternate search behaviour. Custom template options ~~~~~~~~~~~~~~~~~~~~~~~ Loading Loading @@ -1102,6 +1105,39 @@ templates used by the :class:`ModelAdmin` views: else: return ['name'] .. method:: ModelAdmin.get_search_results(self, request, queryset, search_term) .. versionadded:: 1.6 The ``get_search_results`` method modifies the list of objects displayed in to those that match the provided search term. It accepts the request, a queryset that applies the current filters, and the user-provided search term. It returns a tuple containing a queryset modified to implement the search, and a boolean indicating if the results may contain duplicates. The default implementation searches the fields named in :attr:`ModelAdmin.search_fields`. This method may be overridden with your own custom search method. For example, you might wish to search by an integer field, or use an external tool such as Solr or Haystack. You must establish if the queryset changes implemented by your search method may introduce duplicates into the results, and return ``True`` in the second element of the return value. For example, to enable search by integer field, you could use:: class PersonAdmin(admin.ModelAdmin): list_display = ('name', 'age') search_fields = ('name',) def get_search_results(self, request, queryset, search_term): queryset, use_distinct = super(PersonAdmin, self).get_search_results(request, queryset, search_term) try: search_term_as_int = int(search_term) queryset |= self.model.objects.filter(age=search_term_as_int) except: pass return queryset, use_distinct .. method:: ModelAdmin.save_related(self, request, form, formsets, change) The ``save_related`` method is given the ``HttpRequest``, the parent Loading tests/admin_views/admin.py +16 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ from .models import (Article, Chapter, Account, Media, Child, Parent, Picture, Gadget, Villain, SuperVillain, Plot, PlotDetails, CyclicOne, CyclicTwo, WorkHour, Reservation, FoodDelivery, RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory, Book, Promo, ChapterXtra1, Pizza, Topping, Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug, Album, Question, Answer, ComplexSortedPerson, PluggableSearchPerson, PrePopulatedPostLargeSlug, AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated, RelatedPrepopulated, UndeletableObject, UserMessenger, Simple, Choice, Loading Loading @@ -530,6 +530,20 @@ class ComplexSortedPersonAdmin(admin.ModelAdmin): colored_name.admin_order_field = 'name' class PluggableSearchPersonAdmin(admin.ModelAdmin): list_display = ('name', 'age') search_fields = ('name',) def get_search_results(self, request, queryset, search_term): queryset, use_distinct = super(PluggableSearchPersonAdmin, self).get_search_results(request, queryset, search_term) try: search_term_as_int = int(search_term) queryset |= self.model.objects.filter(age=search_term_as_int) except: pass return queryset, use_distinct class AlbumAdmin(admin.ModelAdmin): list_filter = ['title'] Loading Loading @@ -733,6 +747,7 @@ site.register(Question) site.register(Answer) site.register(PrePopulatedPost, PrePopulatedPostAdmin) site.register(ComplexSortedPerson, ComplexSortedPersonAdmin) site.register(PluggableSearchPerson, PluggableSearchPersonAdmin) site.register(PrePopulatedPostLargeSlug, PrePopulatedPostLargeSlugAdmin) site.register(AdminOrderedField, AdminOrderedFieldAdmin) site.register(AdminOrderedModelMethod, AdminOrderedModelMethodAdmin) Loading Loading
AUTHORS +1 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,7 @@ answer newbie questions, and generally made Django that much better: Brian Beck <http://blog.brianbeck.com/> Shannon -jj Behrens <http://jjinux.blogspot.com/> Esdras Beleza <linux@esdrasbeleza.com> Božidar Benko <bbenko@gmail.com> Chris Bennett <chrisrbennett@yahoo.com> Danilo Bargen Shai Berger <shai@platonix.com> Loading
django/contrib/admin/options.py +30 −1 Original line number Diff line number Diff line import copy import operator from functools import update_wrapper, partial from django import forms Loading @@ -9,7 +10,7 @@ from django.forms.models import (modelform_factory, modelformset_factory, from django.contrib.contenttypes.models import ContentType from django.contrib.admin import widgets, helpers from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_objects, model_format_dict, NestedObjects) model_format_dict, NestedObjects, lookup_needs_distinct) from django.contrib.admin import validation from django.contrib.admin.templatetags.admin_static import static from django.contrib import messages Loading Loading @@ -255,6 +256,34 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)): """ return self.prepopulated_fields def get_search_results(self, request, queryset, search_term): # Apply keyword searches. def construct_search(field_name): if field_name.startswith('^'): return "%s__istartswith" % field_name[1:] elif field_name.startswith('='): return "%s__iexact" % field_name[1:] elif field_name.startswith('@'): return "%s__search" % field_name[1:] else: return "%s__icontains" % field_name use_distinct = False if self.search_fields and search_term: orm_lookups = [construct_search(str(search_field)) for search_field in self.search_fields] for bit in search_term.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break return queryset, use_distinct def get_queryset(self, request): """ Returns a QuerySet of all model instances that can be edited by the Loading
django/contrib/admin/views/main.py +5 −27 Original line number Diff line number Diff line import operator import sys import warnings from functools import reduce from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured from django.core.paginator import InvalidPage Loading Loading @@ -331,7 +329,7 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)): def get_queryset(self, request): # First, we collect all the declared list filters. (self.filter_specs, self.has_filters, remaining_lookup_params, use_distinct) = self.get_filters(request) filters_use_distinct) = self.get_filters(request) # Then, we let every list filter modify the queryset to its liking. qs = self.root_queryset Loading Loading @@ -378,31 +376,11 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)): ordering = self.get_ordering(request, qs) qs = qs.order_by(*ordering) # Apply keyword searches. def construct_search(field_name): if field_name.startswith('^'): return "%s__istartswith" % field_name[1:] elif field_name.startswith('='): return "%s__iexact" % field_name[1:] elif field_name.startswith('@'): return "%s__search" % field_name[1:] else: return "%s__icontains" % field_name if self.search_fields and self.query: orm_lookups = [construct_search(str(search_field)) for search_field in self.search_fields] for bit in self.query.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] qs = qs.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.lookup_opts, search_spec): use_distinct = True break # Apply search results qs, search_use_distinct = self.model_admin.get_search_results(request, qs, self.query) if use_distinct: # Remove duplicates from results, if neccesary if filters_use_distinct | search_use_distinct: return qs.distinct() else: return qs Loading
docs/ref/contrib/admin/index.txt +36 −0 Original line number Diff line number Diff line Loading @@ -1005,6 +1005,9 @@ subclass:: Performs a full-text match. This is like the default search method but uses an index. Currently this is only available for MySQL. If you need to customize search you can use :meth:`ModelAdmin.get_search_results` to provide additional or alternate search behaviour. Custom template options ~~~~~~~~~~~~~~~~~~~~~~~ Loading Loading @@ -1102,6 +1105,39 @@ templates used by the :class:`ModelAdmin` views: else: return ['name'] .. method:: ModelAdmin.get_search_results(self, request, queryset, search_term) .. versionadded:: 1.6 The ``get_search_results`` method modifies the list of objects displayed in to those that match the provided search term. It accepts the request, a queryset that applies the current filters, and the user-provided search term. It returns a tuple containing a queryset modified to implement the search, and a boolean indicating if the results may contain duplicates. The default implementation searches the fields named in :attr:`ModelAdmin.search_fields`. This method may be overridden with your own custom search method. For example, you might wish to search by an integer field, or use an external tool such as Solr or Haystack. You must establish if the queryset changes implemented by your search method may introduce duplicates into the results, and return ``True`` in the second element of the return value. For example, to enable search by integer field, you could use:: class PersonAdmin(admin.ModelAdmin): list_display = ('name', 'age') search_fields = ('name',) def get_search_results(self, request, queryset, search_term): queryset, use_distinct = super(PersonAdmin, self).get_search_results(request, queryset, search_term) try: search_term_as_int = int(search_term) queryset |= self.model.objects.filter(age=search_term_as_int) except: pass return queryset, use_distinct .. method:: ModelAdmin.save_related(self, request, form, formsets, change) The ``save_related`` method is given the ``HttpRequest``, the parent Loading
tests/admin_views/admin.py +16 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ from .models import (Article, Chapter, Account, Media, Child, Parent, Picture, Gadget, Villain, SuperVillain, Plot, PlotDetails, CyclicOne, CyclicTwo, WorkHour, Reservation, FoodDelivery, RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory, Book, Promo, ChapterXtra1, Pizza, Topping, Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug, Album, Question, Answer, ComplexSortedPerson, PluggableSearchPerson, PrePopulatedPostLargeSlug, AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated, RelatedPrepopulated, UndeletableObject, UserMessenger, Simple, Choice, Loading Loading @@ -530,6 +530,20 @@ class ComplexSortedPersonAdmin(admin.ModelAdmin): colored_name.admin_order_field = 'name' class PluggableSearchPersonAdmin(admin.ModelAdmin): list_display = ('name', 'age') search_fields = ('name',) def get_search_results(self, request, queryset, search_term): queryset, use_distinct = super(PluggableSearchPersonAdmin, self).get_search_results(request, queryset, search_term) try: search_term_as_int = int(search_term) queryset |= self.model.objects.filter(age=search_term_as_int) except: pass return queryset, use_distinct class AlbumAdmin(admin.ModelAdmin): list_filter = ['title'] Loading Loading @@ -733,6 +747,7 @@ site.register(Question) site.register(Answer) site.register(PrePopulatedPost, PrePopulatedPostAdmin) site.register(ComplexSortedPerson, ComplexSortedPersonAdmin) site.register(PluggableSearchPerson, PluggableSearchPersonAdmin) site.register(PrePopulatedPostLargeSlug, PrePopulatedPostLargeSlugAdmin) site.register(AdminOrderedField, AdminOrderedFieldAdmin) site.register(AdminOrderedModelMethod, AdminOrderedModelMethodAdmin) Loading