Opened 11 years ago

Closed 11 years ago

Last modified 11 years ago

#10722 closed (invalid)

Changelist view does not use select_related() on a nullable foreign key

Reported by: mrts Owned by: nobody
Component: contrib.admin Version: master
Severity: Keywords: efficient-admin
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


Given the following

from django.db import models

class Base(models.Model):
    name = models.CharField(max_length=10)
    lots_of_text = models.TextField()

    class Meta:
        abstract = True

    def __unicode__(self):

class A(Base):
    a_field = models.CharField(max_length=10)

class B(Base):
    b_field = models.CharField(max_length=10)

class C(Base):
    a = models.ForeignKey(A)
    b = models.ForeignKey(B)
    is_published = models.BooleanField()

and the following

from django.contrib import admin
from improve_admin.models import A, B, C

class CAdmin(admin.ModelAdmin):
    list_display = ('name', 'a', 'b', 'is_published'), CAdmin)

select_related() is used as documented in , resulting in the following query:

	"improve_admin_c"."id", "improve_admin_c"."name", "improve_admin_c"."lots_of_text", "improve_admin_c"."a_id", "improve_admin_c"."b_id", "improve_admin_c"."is_published", "improve_admin_a"."id", "improve_admin_a"."name", "improve_admin_a"."lots_of_text", "improve_admin_a"."a_field", "improve_admin_b"."id", "improve_admin_b"."name", "improve_admin_b"."lots_of_text", "improve_admin_b"."b_field"
	INNER JOIN "improve_admin_a" ON ("improve_admin_c"."a_id" = "improve_admin_a"."id")
	INNER JOIN "improve_admin_b" ON ("improve_admin_c"."b_id" = "improve_admin_b"."id")
	"improve_admin_c"."id" DESC

Setting one foreign key field to be nullable:

# diff between the original and new
 class C(Base):
-    a = models.ForeignKey(A)
+    a = models.ForeignKey(A, blank=True, null=True)
     b = models.ForeignKey(B)

results in the following SQL:

	"improve_admin_c"."id", "improve_admin_c"."name", "improve_admin_c"."lots_of_text", "improve_admin_c"."a_id", "improve_admin_c"."b_id", "improve_admin_c"."is_published", "improve_admin_b"."id", "improve_admin_b"."name", "improve_admin_b"."lots_of_text", "improve_admin_b"."b_field"
	INNER JOIN "improve_admin_b" ON ("improve_admin_c"."b_id" = "improve_admin_b"."id")
	"improve_admin_c"."id" DESC

and additional n queries for each of the referred a fields:

	"improve_admin_a"."id", "improve_admin_a"."name", "improve_admin_a"."lots_of_text", "improve_admin_a"."a_field"
	"improve_admin_a"."id" = 2

Change History (5)

comment:1 Changed 11 years ago by mrts

Component: Uncategorizeddjango.contrib.admin

comment:2 Changed 11 years ago by Karen Tracey

Resolution: invalid
Status: newclosed

Admin doesn't introspect the model and decide not to use select_related() if null=True on a ForeignKey. Rather, select_related() itself does not follow null=True ForeignKeys, as documented:

"Note that, by default, select_related() does not follow foreign keys that have null=True."

This strikes me as another case where if you want to control the queries the admin is going to issue to this level of detail, you'll need to specify the ModelAdmin queryset() yourself.

comment:3 Changed 11 years ago by mrts

Thanks for pointing that out!

comment:4 Changed 11 years ago by mrts

See #10742

comment:5 Changed 11 years ago by mrts

Keywords: efficient-admin added
Note: See TracTickets for help on using tickets.
Back to Top