On Thursday 06 January 2011, Byron Ruth wrote:
> I am speaking of this particular if statement:
> http://code.djangoproject.com/browser/django/tags/releases/1.2.4/django/db/
> models/sql/query.py#L1051
> 
> There are a few implications of this assumption.

Just to be clear: The assumption is that if:
        1) the user filtered on a related model's field 
        2) with is_null=True, and 
        3) there is a nullable FK in the chain leading to the filtered field
           (this is checked by promote_alias_chain), 
then:
use a left outer join. 

> One being, that if
> the user is actually trying to get the "real" rows that exist with a
> column that has a NULL value, they will get those in addition to all
> the rows generated that are empty as a result of the LEFT OUTER JOIN.
> 
[...]
> 
> This seems like very strange behavior to have by default.

Not at all (given the full set of assumptions as I've described). How else 
would you express your will to have a left join?

Really, it's mostly a case where the Django ORM query language is not as 
expressive as SQL; I think the choice made by Django here is for the more 
common case.

> I suggest
> there be a way to prevent the joins from being promoted to LEFT OUTER
> JOIN when the specifying a QuerySet condition using NULL values.
> 

Besides the suggestion made by Philippe Raoult:
> use exclude(related__field__isnull = False) instead of
> filter(related__field__isnull = True) to avoid this issue.
You can limit the rows to "real" rows by adding a condition such as 
related__pk__isnull=False. With current code, as far as I understand it, this 
will still do a left join, but pick from it only "real" rows; the question 
then becomes one of performance, not correctness.
This also validates the decision to do a left join when in doubt: From there, 
you can restore inner-join semantics. You can't go the other way around.

> Obviously the fact that people have been relying on this behavior for
> 2+ years either proves to be the correct assumption or not many have
> come across these scenarios. I propose exposing a small API that
> allows for keeping INNER JOINs and not promoting simply because of a
> NULL value.
> 

I think a better, more general approach would be to allow explicit setting of 
join methods:

qs.join_methods(rel1=Inner, rel2__rel2A=Left)

My 2 cents,
        Shai.

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to