I posted this in StackOverflow, and haven't gotten an answer there
yet. I'm hoping someone here can answer my question. I don't know if
this represents a django bug, or a lack of understanding on my part.

Link: 
http://stackoverflow.com/questions/7276147/interactions-among-values-annotate-and-aggregation-in-django-querysets

Copy of text:

I have these two models:

class Leaf(models.Model):
    pass

class Root(models.Model):
    leafs = models.ManyToManyField(Leaf)
    species = models.CharField(max_length=20)
    age = models.FloatField()

    objects = CustomQuerySetManager()

    class QuerySet(query.QuerySet):
        def small(self):
            return self.annotate(leaf_count=models.Count('leafs')).\
                filter(leaf_count__lt=6)

        def oldest_each_species(self):
            oldest_each_species = set()
            for d in
self.values('species').annotate(models.Max('age')):
                some_oldest = self.filter(
                    species=d['species'],
                    age=d['age__max']
                    )[0]
                oldest_each_species.add(some_oldest)
            return oldest_each_species

And this test harness:

class AggTest(test.TestCase):
    def set_up(self):
        pass

    def test_surprise(self):
        for i in range(0,2):
            root = models.Root(species='oak', age=i)
            root.save()
            for j in range(0,4):
                leaf = models.Leaf()
                leaf.save()
                root.leafs.add(leaf)

        print "All Roots"
        for root in models.Root.objects.small():
            print root.id, root.species, root.leafs.count()

        print "All oldest"
        for matron in models.Root.objects.oldest_each_species():
            print matron.id, matron.age

        print "All small oldests DOESN'T WORK"
        for matron in
models.Root.objects.small().oldest_each_species():
            print matron.id, matron.age

        print "All small oldests again"
        for matron in models.Root.objects.\
                filter(id__in=models.Root.objects.small()).\
                oldest_each_species():
            print matron.id, matron.age

which prints:

All Roots
1 oak 4
2 oak 4
All oldest
2 1.0
All small oldests DOESN'T WORK
All small oldests again
2 1.0

I was surprised by the result, but the interactions between values(),
annotate(), and aggregate() often surprise me.

Unwrapping my query, I have

Root.objects.annotate(leaf_count=models.Count('leafs')).
    filter(leaf_count__lt=6).values('species').
    annotate(models.Max('age'))

which turns into the SQL

SELECT `autest_root`.`species`, MAX(`autest_root`.`age`) AS
`age__max`
       FROM `autest_root` LEFT OUTER JOIN `autest_root_leafs`
          ON (`autest_root`.`id` = `autest_root_leafs`.`root_id`)
       GROUP BY `autest_root`.`species`, `autest_root`.`species`
       HAVING COUNT(`autest_root_leafs`.`leaf_id`) < 6  ORDER BY NULL

As shown in the test [at "All small oldests again"], I have a work-
around, adding a nested django query on the ids, so I'm not looking
for a work-around, but better work-arounds are welcome.

I would say I don't understand the interactions well enough to even
know if this is a bug in django. If it is, I'll continue to use a work-
around.

If not a bug, I would like a better understanding of these
interactions, and how to manage situations like this. My best guess
would be to have small() return the nested query, to encapsulate the
fact that it is using annotations to do its job.

So:

    * Is this a bug in django?
    * Is there some documentation which explains what I'm seeing?
    * What is the "best practice" for dealing with this kind of
problem?

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.

Reply via email to