Opened 7 years ago

Closed 7 years ago

Last modified 7 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: UI/UX:


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 7 years ago by mrts

  • Component changed from Uncategorized to django.contrib.admin
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

comment:2 Changed 7 years ago by kmtracey

  • Resolution set to invalid
  • Status changed from new to closed

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 7 years ago by mrts

Thanks for pointing that out!

comment:4 Changed 7 years ago by mrts

See #10742

comment:5 Changed 7 years ago by mrts

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