Ticket #14396: 14396_list_select_related.diff

File 14396_list_select_related.diff, 7.6 KB (added by Daniel Roseman, 14 years ago)
  • django/contrib/admin/validation.py

     
    44    _get_foreign_key)
    55from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin
    66from django.contrib.admin.options import HORIZONTAL, VERTICAL
     7from django.contrib.admin.util import lookup_field
    78
    89
    910__all__ = ['validate']
     
    132133                            raise ImproperlyConfigured("%s.readonly_fields[%d], %r is not a callable or an attribute of %r or found in the model %r."
    133134                                % (cls.__name__, idx, field, cls.__name__, model._meta.object_name))
    134135
    135     # list_select_related = False
    136136    # save_as = False
    137137    # save_on_top = False
    138     for attr in ('list_select_related', 'save_as', 'save_on_top'):
     138    for attr in ('save_as', 'save_on_top'):
    139139        if not isinstance(getattr(cls, attr), bool):
    140140            raise ImproperlyConfigured("'%s.%s' should be a boolean."
    141141                    % (cls.__name__, attr))
    142142
     143    # list_select_related = False
     144    if not isinstance(cls.list_select_related, (bool, list, tuple)):
     145        raise ImproperlyConfigured("'%s.list_select_related' should be a boolean, a list or a tuple."
     146                                   % cls.__name__)
    143147
    144148    # inlines = []
    145149    if hasattr(cls, 'inlines'):
     
    169173    fk = _get_foreign_key(parent_model, cls.model, fk_name=cls.fk_name, can_fail=True)
    170174
    171175    # extra = 3
    172     if not isinstance(cls.extra, int):
     176    if not isinstance(getattr(cls, 'extra'), int):
    173177        raise ImproperlyConfigured("'%s.extra' should be a integer."
    174178                % cls.__name__)
    175179
  • django/contrib/admin/views/main.py

     
    33from django.contrib.admin.util import quote
    44from django.core.paginator import Paginator, InvalidPage
    55from django.db import models
     6from django.db.models.query import QuerySet
    67from django.utils.encoding import force_unicode, smart_str
    78from django.utils.translation import ugettext
    89from django.utils.http import urlencode
     
    201202        # with a relationship and the provided queryset doesn't already have
    202203        # select_related defined.
    203204        if not qs.query.select_related:
    204             if self.list_select_related:
     205            if isinstance(self.list_select_related, (list, tuple)):
     206                qs = qs.select_related(*self.list_select_related)
     207            elif self.list_select_related:
    205208                qs = qs.select_related()
    206209            else:
    207210                for field_name in self.list_display:
     
    210213                    except models.FieldDoesNotExist:
    211214                        pass
    212215                    else:
    213                         if isinstance(f.rel, models.ManyToOneRel):
    214                             qs = qs.select_related()
    215                             break
     216                        if isinstance(f.rel, (models.ManyToOneRel, models.OneToOneRel)):
     217                            qs = qs.select_related(field_name)
    216218
    217219        # Set ordering.
    218220        if self.order_field:
  • tests/regressiontests/admin_changelist/tests.py

     
    22from django.contrib.admin.views.main import ChangeList
    33from django.template import Context, Template
    44from django.test import TransactionTestCase
    5 from regressiontests.admin_changelist.models import Child, Parent
     5from regressiontests.admin_changelist.models import Child, Parent, NullableChild
    66
    77class ChangeListTests(TransactionTestCase):
    88    def test_select_related_preserved(self):
     
    1616                m.list_select_related, m.list_per_page, m.list_editable, m)
    1717        self.assertEqual(cl.query_set.query.select_related, {'parent': {'name': {}}})
    1818
     19    def test_select_related_nullable(self):
     20        """
     21        Regression test for #14396: allow list_select_related to explicitly
     22        specify relations to follow.
     23        """
     24        m = NullableChildAdmin(NullableChild, admin.site)
     25        cl = ChangeList(MockRequest(), NullableChild, m.list_display,
     26                        m.list_display_links, m.list_filter, m.date_hierarchy,
     27                        m.search_fields, m.list_select_related, m.list_per_page,
     28                        m.list_editable, m)
     29        self.assertEqual(cl.query_set.query.select_related, {'parent': {}})
     30
    1931    def test_result_list_html(self):
    2032        """
    2133        Verifies that inclusion tag result_list generates a table when with
     
    7688    def queryset(self, request):
    7789        return super(ChildAdmin, self).queryset(request).select_related("parent__name")
    7890
     91class NullableChildAdmin(admin.ModelAdmin):
     92    list_select_related = ('parent',)
     93
    7994class MockRequest(object):
    8095    GET = {}
  • tests/regressiontests/admin_changelist/models.py

     
    66
    77class Child(models.Model):
    88    parent = models.ForeignKey(Parent, editable=False)
    9     name = models.CharField(max_length=30, blank=True)
    10  No newline at end of file
     9    name = models.CharField(max_length=30, blank=True)
     10
     11class NullableChild(models.Model):
     12    parent = models.ForeignKey(Parent, null=True, blank=True)
     13    name = models.CharField(max_length=30, blank=True)
  • tests/regressiontests/modeladmin/tests.py

     
    963963
    964964        self.assertRaisesRegexp(
    965965            ImproperlyConfigured,
    966             "'ValidationTestModelAdmin.list_select_related' should be a boolean.",
     966            "'ValidationTestModelAdmin.list_select_related' should be a boolean, a list or a tuple.",
    967967            validate,
    968968            ValidationTestModelAdmin,
    969969            ValidationTestModel,
     
    974974
    975975        validate(ValidationTestModelAdmin, ValidationTestModel)
    976976
     977        class ValidationTestModelAdmin(ModelAdmin):
     978            list_select_related = ('field1', 'field2')
     979
     980        validate(ValidationTestModelAdmin, ValidationTestModel)
     981
    977982    def test_save_as_validation(self):
    978983
    979984        class ValidationTestModelAdmin(ModelAdmin):
  • docs/ref/contrib/admin/index.txt

     
    476476objects on the admin change list page. This can save you a bunch of database
    477477queries.
    478478
    479 The value should be either ``True`` or ``False``. Default is ``False``.
     479The value should be one of:
    480480
     481* ``True`` or ``False``. If ``True``, the standard ``select_related()`` call
     482  will be used.
     483
     484* A list or tuple of field names. In this case, the value is used as the
     485  argument to :meth:`~django.db.models.QuerySet.select_related` which determines
     486  which relations should be followed.
     487
     488The default is ``False``.
     489
    481490Note that Django will use :meth:`~django.db.models.QuerySet.select_related`,
    482491regardless of this setting, if one of the ``list_display`` fields is a
    483 ``ForeignKey``.
     492``ForeignKey`` or ``OneToOneField``.
    484493
    485494.. attribute:: ModelAdmin.inlines
    486495
Back to Top