﻿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
12687	Using exclude on a queryset with an annotate field give attribute error.	AsgeirM		"When using '''annotate''' on the ''queryset'' of a model that has a ''foreignkey'' relation, you may not use '''exclude''' towards relations afterwards. (See appended code for example)

If you were to use '''annotate''' on the model that is pointed at with the ''foreignkey'', '''exclude''' with relations still work.

'''Exclude'''ing before using annotate always works.

Tested with django.VERSION (1, 1, 0, 'final', 0) on python 2.4.3,
was also tested on the 2010.01.22 with the latest SVN.

== SQL for creating example table ==
{{{
#!sql
BEGIN;
CREATE TABLE ""rootnode"" (
    ""id"" serial NOT NULL PRIMARY KEY,
    ""somefield"" integer CHECK (""somefield"" >= 0) NOT NULL);
CREATE TABLE ""childnode"" (
    ""id"" serial NOT NULL PRIMARY KEY,
    ""parent_id"" integer NOT NULL REFERENCES ""rootnode"" (""id"") DEFERRABLE INITIALLY DEFERRED,
    ""oddfield"" integer CHECK (""oddfield"" >= 0) NOT NULL,
    ""evenfield"" integer CHECK (""evenfield"" >= 0) NOT NULL);
COMMIT;
}}}

== Code that demonstrates the AttributeError ==
{{{
#!python
from django.db import models
from django.db.models import Min

class RootNode(models.Model):
	somefield = models.PositiveIntegerField()

	class Meta:
		db_table = u'rootnode'

class ChildNode(models.Model):
	parent = models.ForeignKey('RootNode', null = False)
	oddfield = models.PositiveIntegerField()
	evenfield = models.PositiveIntegerField()

	class Meta:
		db_table = u'childnode'

rn1 = RootNode()
rn1.somefield = 2
rn1.save()

rn2 = RootNode()
rn2.somefield = 3
rn2.save()

cn1 = ChildNode()
cn1.parent = rn1
cn1.oddfield = 43
cn1.evenfield = 42
cn1.save()

# This node has even / odd swaped for 'testing'
cn2 = ChildNode()
cn2.parent = rn1
cn2.oddfield = 42
cn2.evenfield = 43
cn2.save()

# Excluding before the annotates always works.
q_set = ChildNode.objects.all().annotate(min_somefield=Min('parent__somefield'))

# Works
print q_set.exclude(oddfield=43).query
print q_set.exclude(min_somefield=3)

# Gives wrong result (empty set)
print q_set.exclude(parent__somefield=1)

# Attribute error
print q_set.exclude(parent__somefield=1).query

# Attribute error
print q_set.exclude(parent__somefield=2).count()
}}}

== Tracelog ==
{{{
AttributeError                            Traceback (most recent call last)

/usr/lib/python2.4/site-packages/django/db/models/sql/query.pyc in __str__(self)
    111         done by the database interface at execution time.
    112         """"""
--> 113         sql, params = self.as_sql()
    114         return sql % params
    115 

/usr/lib/python2.4/site-packages/django/db/models/sql/query.pyc in as_sql(self, with_limits, with_col_aliases)
    402 
    403         qn = self.quote_name_unless_alias
--> 404         where, w_params = self.where.as_sql(qn=qn)
    405         having, h_params = self.having.as_sql(qn=qn)
    406         params = []

/usr/lib/python2.4/site-packages/django/db/models/sql/where.pyc in as_sql(self, qn)
     98             try:
     99                 if hasattr(child, 'as_sql'):
--> 100                     sql, params = child.as_sql(qn=qn)
    101                 else:
    102                     # A leaf node in the tree.

/usr/lib/python2.4/site-packages/django/db/models/sql/where.pyc in as_sql(self, qn)
     98             try:
     99                 if hasattr(child, 'as_sql'):
--> 100                     sql, params = child.as_sql(qn=qn)
    101                 else:
    102                     # A leaf node in the tree.

/usr/lib/python2.4/site-packages/django/db/models/sql/where.pyc in as_sql(self, qn)
     98             try:
     99                 if hasattr(child, 'as_sql'):
--> 100                     sql, params = child.as_sql(qn=qn)
    101                 else:
    102                     # A leaf node in the tree.

/usr/lib/python2.4/site-packages/django/db/models/sql/where.pyc in as_sql(self, qn)
    101                 else:
    102                     # A leaf node in the tree.
--> 103                     sql, params = self.make_atom(child, qn)
    104 
    105             except EmptyResultSet:

/usr/lib/python2.4/site-packages/django/db/models/sql/where.pyc in make_atom(self, child, qn)
    148         if isinstance(lvalue, tuple):
    149             # A direct database column lookup.
--> 150             field_sql = self.sql_for_columns(lvalue, qn)
    151         else:
    152             # A smart object with an as_sql() method.

/usr/lib/python2.4/site-packages/django/db/models/sql/where.pyc in sql_for_columns(self, data, qn)
    200         table_alias, name, db_type = data
    201         if table_alias:
--> 202             lhs = '%s.%s' % (qn(table_alias), qn(name))
    203         else:
    204             lhs = qn(name)

/usr/lib/python2.4/site-packages/django/db/models/sql/query.pyc in quote_name_unless_alias(self, name)
    173             self.quote_cache[name] = name
    174             return name
--> 175         r = self.connection.ops.quote_name(name)
    176         self.quote_cache[name] = r
    177         return r

/usr/lib/python2.4/site-packages/django/db/backends/postgresql/operations.pyc in quote_name(self, name)
     61 
     62     def quote_name(self, name):
---> 63         if name.startswith('""') and name.endswith('""'):
     64             return name # Quoting once is enough.
     65         return '""%s""' % name

AttributeError: 'NoneType' object has no attribute 'startswith'
}}}"		closed	Database layer (models, ORM)	1.2		fixed	annotate exclude AttributeError		Accepted	0	0	0	0	0	0
