Opened 14 years ago

Closed 13 years ago

Last modified 11 years ago

#12687 closed (fixed)

Using exclude on a queryset with an annotate field give attribute error.

Reported by: AsgeirM Owned by:
Component: Database layer (models, ORM) Version: 1.2
Severity: Keywords: annotate exclude AttributeError
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

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.

Excludeing 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

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

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'

Change History (8)

comment:1 by Russell Keith-Magee, 14 years ago

milestone: 1.2
Triage Stage: UnreviewedAccepted

comment:2 by Russell Keith-Magee, 14 years ago

milestone: 1.21.3

Not critical for 1.2.

comment:3 by anonymous, 14 years ago

Version: 1.11.2

Also attribute error in Django 1.2:

print q_set.filter(~Q(parent__somefield=1)).query

comment:4 by Petr Marhoun <petr.marhoun@…>, 14 years ago

Ticket #13356 was closed as duplicate of this patch - It is true that is seems to be quite similar and there is more information here. But I think that it has more simple example of the problem and it could be also here.

Following code works :

from django.contrib.auth.models import User
from django.db.models import Count
User.objects.filter(id__in=[]).annotate(Count('groups'))
[]
User.objects.filter(id__in=[]).count()
0

But following code raise EmptyResultSet:

User.objects.filter(id__in=[]).annotate(Count('groups')).count()

comment:5 by Alex Gaynor, 13 years ago

Resolution: fixed
Status: newclosed

(In [14586]) Fixed #12687 -- fixed an issue with aggregates and counts in conjunction with annotations where the QuerySet was provably empty.

comment:6 by Alex Gaynor, 13 years ago

(In [14587]) [1.2.X] Fixed #12687 -- fixed an issue with aggregates and counts in conjunction with annotations where the QuerySet was provably empty. Backport of [14586].

comment:7 by Jacob, 13 years ago

milestone: 1.3

Milestone 1.3 deleted

comment:8 by Anssi Kääriäinen, 11 years ago

Component: ORM aggregationDatabase layer (models, ORM)
Note: See TracTickets for help on using tickets.
Back to Top