Django

Code

Ticket #3665 (closed: duplicate)

Opened 1 year ago

Last modified 1 year ago

select_related() should generate LEFT JOINs for foreign keys with null=True

Reported by: semenov@inetss.com Assigned to: mtredinnick
Milestone: Component: Core framework
Version: SVN Keywords: orm select_related left join
Cc: Triage Stage: Accepted
Has patch: 0 Needs documentation: 0
Needs tests: 0 Patch needs improvement: 0

Description

Consider a (simplified) real-life example:

class Ticket(models.Model): 
    is_resolved = models.BooleanField() 
    resolved_by = models.ForeignKey(User, null=True)

...a view which is showing all resolved tickets:

def resolved_tickets(request): 
    tickets = Ticket.objects.filter(is_resolved=True).select_related() 
    return HttpResponse(......) 

...and a template which is doing something like:

{% for t in tickets %} 
    {{ t }} resolved by {{ t.user }} 
{% endfor %} 

(I believe this is a very common use case; I've run into it several times while developing my first application!)

Since user field has null=True, select_related() doesn't do its job and the code generates N+1 queries. However, from SQL point of view, the whole bunch of queries can be replaced with a single simple LEFT JOIN query, and I would expect Django ORM to allow this.

I realize that under some circumstances, LEFT JOINs can be costly. I also realize that LEFT JOINs can hardly traverse through several nesting levels. That's why, I would like to suggest the following (unobtrusive and backwards-compatible!) behaviour:

1. If select_related() called with depth argument (as of [4645]), the last level of recursion uses LEFT JOINs instead of INNER JOINs for foreign keys.

2. If select_related() called with fields argument (once the fields suggestion from #3275 is committed) and the list of fields contains nullable foreign key(s), these particular key(s) are joined using LEFT JOIN, and the recursion stops for them.

As far as I understand, this will neither break any old code, nor make any perfomance impact; while being very useful in terms of database load optimization.

Attachments

Change History

03/06/07 07:26:13 changed by Simon G. <dev@simon.net.nz>

  • needs_better_patch changed.
  • stage changed from Unreviewed to Design decision needed.
  • summary changed from select_related() to generate LEFT JOINs for foreign keys with null=True to select_related() should generate LEFT JOINs for foreign keys with null=True.
  • needs_tests changed.
  • needs_docs changed.

03/08/07 03:43:52 changed by Ilya Semenov <semenov@inetss.com>

Thinking more about the issue, I came up with the more generic proposal:

select_related() should always use LEFT JOINs for all nullable foreign keys. The recursion should not proceed to these fields.

Two changes that I mentioned in the ticket description will therefore be just special cases of this generic rule.

03/08/07 03:50:49 changed by mtredinnick

  • owner changed from adrian to mtredinnick.
  • stage changed from Design decision needed to Accepted.

This will be fixed in the QuerySet rewrite that is happening at the moment. We can definitely cut down on the number of queries there. Your logic, by the way, is correct -- always use left outer joins in this case.

03/08/07 10:46:17 changed by mir@noris.de

mtredinnick: maybe this should be a duplicate of #3592?

03/08/07 11:11:34 changed by ubernostrum

  • status changed from new to closed.
  • resolution set to duplicate.

Certainly looks to be. Closing as duplicate of #3592.


Add/Change #3665 (select_related() should generate LEFT JOINs for foreign keys with null=True)




Change Properties
Action