Code

Opened 4 years ago

Closed 3 years ago

Last modified 14 months 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: UI/UX:

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'

Attachments (0)

Change History (8)

comment:1 Changed 4 years ago by russellm

  • milestone set to 1.2
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Accepted

comment:2 Changed 4 years ago by russellm

  • milestone changed from 1.2 to 1.3

Not critical for 1.2.

comment:3 Changed 4 years ago by anonymous

  • Version changed from 1.1 to 1.2

Also attribute error in Django 1.2:

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

comment:4 Changed 3 years ago by Petr Marhoun <petr.marhoun@…>

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 Changed 3 years ago by Alex

  • Resolution set to fixed
  • Status changed from new to closed

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

comment:6 Changed 3 years ago by Alex

(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 Changed 3 years ago by jacob

  • milestone 1.3 deleted

Milestone 1.3 deleted

comment:8 Changed 14 months ago by akaariai

  • Component changed from ORM aggregation to Database layer (models, ORM)

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.