﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
13195	Generic relations produce wrong queries with Q objects	Claude Paroz	nobody	"Here is my test case model:
{{{
from django.db import models
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType

class BaseObject(models.Model):
    status      = models.CharField(max_length=10)
    permissions = generic.GenericRelation(""Permission"")

class Permission(models.Model):
    content_type   = models.ForeignKey(ContentType, related_name=""permissions"")
    object_id      = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    perm           = models.CharField(max_length=10)
}}}

When I make queries OR'ed with Q objects, the join condition on content_type_id is placed in a fragile position which can produce wrong queries. By example, the query {{{BaseObject.objects.filter(Q(status='public') | Q(permissions__perm='rw'))}}} is producing this (wrong) SQL query:
{{{
SELECT ""testapp_baseobject"".""id"", ""testapp_baseobject"".""status"" FROM ""testapp_baseobject""
    LEFT OUTER JOIN ""testapp_permission"" ON (""testapp_baseobject"".""id"" = ""testapp_permission"".""object_id"")
    WHERE ((""testapp_baseobject"".""status"" = 'public'  OR ""testapp_permission"".""perm"" = 'rw' ) AND ""testapp_permission"".""content_type_id"" = 8 )
}}}

If I simply inverse the Q objects (which should produce identical results) {{{BaseObject.objects.filter(Q(permissions__perm='rw') | Q(status='public'))}}}, the SQL query (correct by accident?) is:
{{{
SELECT ""testapp_baseobject"".""id"", ""testapp_baseobject"".""status"" FROM ""testapp_baseobject""
    LEFT OUTER JOIN ""testapp_permission"" ON (""testapp_baseobject"".""id"" = ""testapp_permission"".""object_id"")
    WHERE ((""testapp_permission"".""perm"" = 'rw'  AND ""testapp_permission"".""content_type_id"" = 8 ) OR ""testapp_baseobject"".""status"" = 'public' )
}}}

In my opinion, the right thing (cleaner and more robust) would be to add the content_type_id clause inside the OUTER JOIN ""ON"" clause:
{{{
SELECT ""testapp_baseobject"".""id"", ""testapp_baseobject"".""status"" FROM ""testapp_baseobject""
    LEFT OUTER JOIN ""testapp_permission"" ON (""testapp_baseobject"".""id"" = ""testapp_permission"".""object_id"" AND ""testapp_permission"".""content_type_id"" = 8)
    WHERE (""testapp_permission"".""perm"" = 'rw'  OR ""testapp_baseobject"".""status"" = 'public' )
}}}"		closed	Contrib apps	1.1		duplicate			Unreviewed	0	0	0	0	0	0
