Code

Ticket #14396: 14396_list_select_related.patch

File 14396_list_select_related.patch, 7.6 KB (added by danielr, 3 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