summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorMarc Tamlyn <marc.tamlyn@gmail.com>2013-06-06 01:27:05 -0700
committerMarc Tamlyn <marc.tamlyn@gmail.com>2013-06-06 01:27:05 -0700
commit9ed971f4f1d2f05ecf7e2760556259eb2dca85f8 (patch)
treea980fbf9d9a9039c8cdce2e18fe0ddfb35585dda /django
parent31fd64ad8a98d7de0acb9144ae6f7bd124700cb0 (diff)
parent0fd9f7c95f748764867dc148a2bacef807466d85 (diff)
Merge pull request #1245 from oinopion/list_select_related
Fixed #19080 -- Fine-grained control over select_related in admin
Diffstat (limited to 'django')
-rw-r--r--django/contrib/admin/validation.py10
-rw-r--r--django/contrib/admin/views/main.py44
2 files changed, 35 insertions, 19 deletions
diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py
index 59c5ad35ef..222d433e53 100644
--- a/django/contrib/admin/validation.py
+++ b/django/contrib/admin/validation.py
@@ -310,8 +310,14 @@ class ModelAdminValidator(BaseValidator):
% (cls.__name__, idx, field))
def validate_list_select_related(self, cls, model):
- " Validate that list_select_related is a boolean. "
- check_type(cls, 'list_select_related', bool)
+ " Validate that list_select_related is a boolean, a list or a tuple. "
+ list_select_related = getattr(cls, 'list_select_related', None)
+ if list_select_related:
+ types = (bool, tuple, list)
+ if not isinstance(list_select_related, types):
+ raise ImproperlyConfigured("'%s.list_select_related' should be "
+ "either a bool, a tuple or a list" %
+ cls.__name__)
def validate_list_per_page(self, cls, model):
" Validate that list_per_page is an integer. "
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index dbed21265c..8ea7e10fc0 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -356,36 +356,46 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)):
# ValueError, ValidationError, or ?.
raise IncorrectLookupParameters(e)
- # Use select_related() if one of the list_display options is a field
- # with a relationship and the provided queryset doesn't already have
- # select_related defined.
if not qs.query.select_related:
- if self.list_select_related:
- qs = qs.select_related()
- else:
- for field_name in self.list_display:
- try:
- field = self.lookup_opts.get_field(field_name)
- except models.FieldDoesNotExist:
- pass
- else:
- if isinstance(field.rel, models.ManyToOneRel):
- qs = qs.select_related()
- break
+ qs = self.apply_select_related(qs)
# Set ordering.
ordering = self.get_ordering(request, qs)
qs = qs.order_by(*ordering)
# Apply search results
- qs, search_use_distinct = self.model_admin.get_search_results(request, qs, self.query)
+ qs, search_use_distinct = self.model_admin.get_search_results(
+ request, qs, self.query)
- # Remove duplicates from results, if neccesary
+ # Remove duplicates from results, if necessary
if filters_use_distinct | search_use_distinct:
return qs.distinct()
else:
return qs
+ def apply_select_related(self, qs):
+ if self.list_select_related is True:
+ return qs.select_related()
+
+ if self.list_select_related is False:
+ if self.has_related_field_in_list_display():
+ return qs.select_related()
+
+ if self.list_select_related:
+ return qs.select_related(*self.list_select_related)
+ return qs
+
+ def has_related_field_in_list_display(self):
+ for field_name in self.list_display:
+ try:
+ field = self.lookup_opts.get_field(field_name)
+ except models.FieldDoesNotExist:
+ pass
+ else:
+ if isinstance(field.rel, models.ManyToOneRel):
+ return True
+ return False
+
def url_for_result(self, result):
pk = getattr(result, self.pk_attname)
return reverse('admin:%s_%s_change' % (self.opts.app_label,