From a1d160e2ea22cb010b276cfc2a085f9cd3d81b22 Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Sun, 14 Oct 2007 22:38:54 +0000 Subject: queryset-refactor: Fixed a large bag of order_by() problems. This also picked up a small bug in some twisted select_related() handling. Introduces a new syntax for cross-model ordering: foo__bar__baz, using field names, instead of a strange combination of table names and field names. This might turn out to be backwards compatible (although the old syntax leads to bugs and is not to be recommended). Still to come: fixes for extra() handling, since the new syntax can't handle that and doc updates. Things are starting to get a bit slow here, so we might eventually have to remove ordering by many-many and other multiple-result fields, since they don't make a lot of sense in any case. For now, it's legal. Refs #2076, #2874, #3002 (although the admin bit doesn't work yet). git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6510 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- tests/modeltests/reserved_names/models.py | 2 - tests/regressiontests/queries/models.py | 128 +++++++++++++++++++++++++++--- 2 files changed, 117 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/tests/modeltests/reserved_names/models.py b/tests/modeltests/reserved_names/models.py index a11b8d9f88..f698b5bc49 100644 --- a/tests/modeltests/reserved_names/models.py +++ b/tests/modeltests/reserved_names/models.py @@ -45,8 +45,6 @@ h b >>> print v.where 2005-01-01 ->>> Thing.objects.order_by('select.when') -[, ] >>> Thing.objects.dates('where', 'year') [datetime.datetime(2005, 1, 1, 0, 0), datetime.datetime(2006, 1, 1, 0, 0)] diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index 64baacc32d..c775772ada 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -1,5 +1,5 @@ """ -Various combination queries that have been problematic in the past. +Various complex queries that have been problematic in the past. """ from django.db import models @@ -12,9 +12,29 @@ class Tag(models.Model): def __unicode__(self): return self.name +class Note(models.Model): + note = models.CharField(maxlength=100) + + class Meta: + ordering = ['note'] + + def __unicode__(self): + return self.note + +class ExtraInfo(models.Model): + info = models.CharField(maxlength=100) + note = models.ForeignKey(Note) + + class Meta: + ordering = ['info'] + + def __unicode__(self): + return self.info + class Author(models.Model): name = models.CharField(maxlength=10) num = models.IntegerField(unique=True) + extra = models.ForeignKey(ExtraInfo) def __unicode__(self): return self.name @@ -23,6 +43,10 @@ class Item(models.Model): name = models.CharField(maxlength=10) tags = models.ManyToManyField(Tag, blank=True, null=True) creator = models.ForeignKey(Author) + note = models.ForeignKey(Note) + + class Meta: + ordering = ['-note', 'name'] def __unicode__(self): return self.name @@ -34,6 +58,26 @@ class Report(models.Model): def __unicode__(self): return self.name +class Ranking(models.Model): + rank = models.IntegerField() + author = models.ForeignKey(Author) + + class Meta: + # A complex ordering specification. Should stress the system a bit. + ordering = ('author__extra__note', 'author__name', 'rank') + + def __unicode__(self): + return '%d: %s' % (self.rank, self.author.name) + +class Cover(models.Model): + title = models.CharField(maxlength=50) + item = models.ForeignKey(Item) + + class Meta: + ordering = ['item'] + + def __unicode__(self): + return self.title __test__ = {'API_TESTS':""" >>> t1 = Tag(name='t1') @@ -47,25 +91,38 @@ __test__ = {'API_TESTS':""" >>> t5 = Tag(name='t5', parent=t3) >>> t5.save() - ->>> a1 = Author(name='a1', num=1001) +>>> n1 = Note(note='n1') +>>> n1.save() +>>> n2 = Note(note='n2') +>>> n2.save() +>>> n3 = Note(note='n3') +>>> n3.save() + +Create these out of order so that sorting by 'id' will be different to sorting +by 'info'. Helps detect some problems later. +>>> e2 = ExtraInfo(info='e2', note=n2) +>>> e2.save() +>>> e1 = ExtraInfo(info='e1', note=n1) +>>> e1.save() + +>>> a1 = Author(name='a1', num=1001, extra=e1) >>> a1.save() ->>> a2 = Author(name='a2', num=2002) +>>> a2 = Author(name='a2', num=2002, extra=e1) >>> a2.save() ->>> a3 = Author(name='a3', num=3003) +>>> a3 = Author(name='a3', num=3003, extra=e2) >>> a3.save() ->>> a4 = Author(name='a4', num=4004) +>>> a4 = Author(name='a4', num=4004, extra=e2) >>> a4.save() ->>> i1 = Item(name='one', creator=a1) +>>> i1 = Item(name='one', creator=a1, note=n3) >>> i1.save() >>> i1.tags = [t1, t2] ->>> i2 = Item(name='two', creator=a2) +>>> i2 = Item(name='two', creator=a2, note=n2) >>> i2.save() >>> i2.tags = [t1, t3] ->>> i3 = Item(name='three', creator=a2) +>>> i3 = Item(name='three', creator=a2, note=n3) >>> i3.save() ->>> i4 = Item(name='four', creator=a4) +>>> i4 = Item(name='four', creator=a4, note=n3) >>> i4.save() >>> i4.tags = [t4] @@ -74,6 +131,20 @@ __test__ = {'API_TESTS':""" >>> r2 = Report(name='r2', creator=a3) >>> r2.save() +Ordering by 'rank' gives us rank2, rank1, rank3. Ordering by the Meta.ordering +will be rank3, rank2, rank1. +>>> rank1 = Ranking(rank=2, author=a2) +>>> rank1.save() +>>> rank2 = Ranking(rank=1, author=a3) +>>> rank2.save() +>>> rank3 = Ranking(rank=3, author=a1) +>>> rank3.save() + +>>> c1 = Cover(title="first", item=i4) +>>> c1.save() +>>> c2 = Cover(title="second", item=i2) +>>> c2.save() + Bug #1050 >>> Item.objects.filter(tags__isnull=True) [] @@ -119,7 +190,7 @@ Bug #1878, #2939 # Create something with a duplicate 'name' so that we can test multi-column # cases (which require some tricky SQL transformations under the covers). ->>> xx = Item(name='four', creator=a2) +>>> xx = Item(name='four', creator=a2, note=n1) >>> xx.save() >>> Item.objects.exclude(name='two').values('creator', 'name').distinct().count() 4 @@ -206,5 +277,40 @@ Bug #2400 Bug #2496 >>> Item.objects.extra(tables=['queries_author']).select_related().order_by('name')[:1] [] + +Bug #2076 +# Ordering on related tables should be possible, even if the table is not +# otherwise involved. +>>> Item.objects.order_by('note__note', 'name') +[, , , ] + +# Ordering on a related field should use the remote model's default ordering as +# a final step. +>>> Author.objects.order_by('extra', '-name') +[, , , ] + +# If the remote model does not have a default ordering, we order by its 'id' +# field. +>>> Item.objects.order_by('creator', 'name') +[, , , ] + +# Cross model ordering is possible in Meta, too. +>>> Ranking.objects.all() +[, , ] +>>> Ranking.objects.all().order_by('rank') +[, , ] + +>>> Cover.objects.all() +[, ] + +Bugs #2874, #3002 +>>> qs = Item.objects.select_related().order_by('note__note', 'name') +>>> list(qs) +[, , , ] + +# This is also a good select_related() test because there are multiple Note +# entries in the SQL. The two Note items should be different. +>>> qs[0].note, qs[0].creator.extra.note +(, ) """} -- cgit v1.3