Anssi,

Thanks for helping.
I'm sorry to say that your answer went somewhat over my head, my 
proficiency with SQL is lacking.

What I understood from your explanation:
 - A filter/exclude that traverses a 1:N relationship(such as foreign key) 
should target the same row with all of its criteria(kwargs).
 - Complex queries don't work correctly in exclude when using relationships 
in 1.5.x
 - Complex queries don't work correctly in exclude when using relationships 
in 1.6.x

Did I understand correctly?

If that was the whole of the situation I would be ok, I can work around 
this issue with multiple exclude statements, such as:
bees = B.objects.exclude(a__confirmation=False, a__state=1)
bees = bees.exclude(a__confirmation__isnull=True, a__state=1)
That should be equivalent to what I was trying to do with:
confirm_q = Q(a__confirmation=False) | Q(a__confirmation__isnull=True)
bees = B.objects.exclude(confirm_q, a__state=1)

But my solution of splitting the Q into two queries didn't work, for either 
1.5.5 or 1.6rc1.
Did I miss something?

Gastal

On Monday, November 4, 2013 2:43:55 PM UTC-2, Anssi Kääriäinen wrote:
>
> On Monday, November 4, 2013 6:06:55 PM UTC+2, Anssi Kääriäinen wrote:
>>
>> I'll look into this. 
>>
>
> The situation is that this query didn't work properly in 1.5.x, but this 
> doesn't work properly in 1.6.x either.
>
> The basic problem here is that in 1.5.x .exclude(Q(anything)) didn't work 
> correctly. Using the example models, try these equivalent queries:
>     bees1 = B.objects.exclude(Q(a__state=1))
>     bees2 = B.objects.exclude(a__state=1)
>
> bees1 produces querystr:
>     SELECT "new_basic_b"."id" FROM "new_basic_b" INNER JOIN "new_basic_a" 
> ON ("new_basic_b"."id" = "new_basic_a"."b_id") WHERE NOT 
> ("new_basic_a"."state" = 1 )
>
> while bees2 produces:
>     SELECT "new_basic_b"."id" FROM "new_basic_b" WHERE NOT 
> (("new_basic_b"."id" IN (SELECT U1."b_id" FROM "new_basic_a" U1 WHERE 
> (U1."state" = 1  AND U1."b_id" IS NOT NULL)) AND "new_basic_b"."id" IS NOT 
> NULL))
>
> Note that bees2 has correctly subquery in it. The bees1 query will produce 
> incorrect results.
>
> Now, for the example query, the same "exclude hiding" happens for 
> Q(a__confirmation=False) | Q(a__confirmation__isnull=True). Due to this the 
> produced query is:
>     SELECT "new_basic_b"."id" FROM "new_basic_b" LEFT OUTER JOIN 
> "new_basic_a" ON ("new_basic_b"."id" = "new_basic_a"."b_id") WHERE NOT 
> (("new_basic_a"."confirmation" = False  OR "new_basic_a"."confirmation" IS 
> NULL) AND ("new_basic_b"."id" IN (SELECT U1."b_id" FROM "new_basic_a" U1 
> WHERE (U1."state" = 1  AND U1."b_id" IS NOT NULL)) AND "new_basic_b"."id" 
> IS NOT NULL))
>
> Note the LEFT OUTER JOIN for the ORed condition, but subquery for the 
> state condition. That is incorrect, both filters should be in the same 
> subquery.
>
> In 1.6.x the situation is, well, different. The generated query is:
>     SELECT "new_basic_b"."id" FROM "new_basic_b" WHERE NOT 
> (("new_basic_b"."id" IN (SELECT U1."b_id" FROM "new_basic_a" U1 WHERE 
> U1."confirmation" = False ) OR "new_basic_b"."id" IN (SELECT U0."id" FROM 
> "new_basic_b" U0 LEFT OUTER JOIN "new_basic_a" U1 ON ( U0."id" = U1."b_id" 
> ) WHERE U1."confirmation" IS NULL)) AND "new_basic_b"."id" IN (SELECT 
> U1."b_id" FROM "new_basic_a" U1 WHERE U1."state" = 1 ))
>
> Now we have each of the conditions correctly in a subquery, but in 
> different subqueries which isn't correct (filters inside single 
> .filter()/.exclude() should target the same row when having multiple 
> clauses for the same multivalued relation).
>
> Complex filters in .exclude() didn't work correctly, and do not work 
> correctly in the upcoming 1.6 either. If the results were correct in 1.5 
> that was luck, not a result of Django generating the correct query. It 
> should be possible to construct data that highlights the problem in the 1.5 
> version of the query.
>
>  - Anssi
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/e3398719-7be8-4624-9bac-717dd1553612%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to