[3.2.x] Refs #32682 -- Renamed use_distinct variable to may_have_duplicates.

QuerySet.distinct() is not the only way to avoid duplicate, it's also
not preferred.

Backport of cd74aad90e from main
This commit is contained in:
Mariusz Felisiak 2021-04-26 09:30:40 +02:00
parent 7ad7034054
commit fbea64b8ce
4 changed files with 23 additions and 15 deletions

View File

@ -1019,7 +1019,7 @@ class ModelAdmin(BaseModelAdmin):
# Otherwise, use the field with icontains.
return "%s__icontains" % field_name
use_distinct = False
may_have_duplicates = False
search_fields = self.get_search_fields(request)
if search_fields and search_term:
orm_lookups = [construct_search(str(search_field))
@ -1030,9 +1030,11 @@ class ModelAdmin(BaseModelAdmin):
or_queries = [models.Q(**{orm_lookup: bit})
for orm_lookup in orm_lookups]
queryset = queryset.filter(reduce(operator.or_, or_queries))
use_distinct |= any(lookup_needs_distinct(self.opts, search_spec) for search_spec in orm_lookups)
return queryset, use_distinct
may_have_duplicates |= any(
lookup_needs_distinct(self.opts, search_spec)
for search_spec in orm_lookups
)
return queryset, may_have_duplicates
def get_preserved_filters(self, request):
"""

View File

@ -122,7 +122,7 @@ class ChangeList:
def get_filters(self, request):
lookup_params = self.get_filters_params()
use_distinct = False
may_have_duplicates = False
has_active_filters = False
for key, value in lookup_params.items():
@ -157,7 +157,7 @@ class ChangeList:
# processes. If that happened, check if distinct() is needed to
# remove duplicate results.
if lookup_params_count > len(lookup_params):
use_distinct = use_distinct or lookup_needs_distinct(self.lookup_opts, field_path)
may_have_duplicates |= lookup_needs_distinct(self.lookup_opts, field_path)
if spec and spec.has_output():
filter_specs.append(spec)
if lookup_params_count > len(lookup_params):
@ -203,9 +203,9 @@ class ChangeList:
try:
for key, value in lookup_params.items():
lookup_params[key] = prepare_lookup_value(key, value)
use_distinct = use_distinct or lookup_needs_distinct(self.lookup_opts, key)
may_have_duplicates |= lookup_needs_distinct(self.lookup_opts, key)
return (
filter_specs, bool(filter_specs), lookup_params, use_distinct,
filter_specs, bool(filter_specs), lookup_params, may_have_duplicates,
has_active_filters,
)
except FieldDoesNotExist as e:
@ -445,7 +445,7 @@ class ChangeList:
self.filter_specs,
self.has_filters,
remaining_lookup_params,
filters_use_distinct,
filters_may_have_duplicates,
self.has_active_filters,
) = self.get_filters(request)
# Then, we let every list filter modify the queryset to its liking.
@ -480,7 +480,9 @@ class ChangeList:
qs = qs.order_by(*ordering)
# Apply search results
qs, search_use_distinct = self.model_admin.get_search_results(request, qs, self.query)
qs, search_may_have_duplicates = self.model_admin.get_search_results(
request, qs, self.query,
)
# Set query string for clearing all filters.
self.clear_all_filters_qs = self.get_query_string(
@ -488,7 +490,7 @@ class ChangeList:
remove=self.get_filters_params(),
)
# Remove duplicates from results, if necessary
if filters_use_distinct | search_use_distinct:
if filters_may_have_duplicates | search_may_have_duplicates:
return qs.distinct()
else:
return qs

View File

@ -1586,14 +1586,16 @@ templates used by the :class:`ModelAdmin` views:
search_fields = ('name',)
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super().get_search_results(request, queryset, search_term)
queryset, may_have_duplicates = super().get_search_results(
request, queryset, search_term,
)
try:
search_term_as_int = int(search_term)
except ValueError:
pass
else:
queryset |= self.model.objects.filter(age=search_term_as_int)
return queryset, use_distinct
return queryset, may_have_duplicates
This implementation is more efficient than ``search_fields =
('name', '=age')`` which results in a string comparison for the numeric

View File

@ -660,14 +660,16 @@ class PluggableSearchPersonAdmin(admin.ModelAdmin):
search_fields = ('name',)
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super().get_search_results(request, queryset, search_term)
queryset, may_have_duplicates = super().get_search_results(
request, queryset, search_term,
)
try:
search_term_as_int = int(search_term)
except ValueError:
pass
else:
queryset |= self.model.objects.filter(age=search_term_as_int)
return queryset, use_distinct
return queryset, may_have_duplicates
class AlbumAdmin(admin.ModelAdmin):