Django

Code

root/django/trunk/tests/regressiontests/queries/models.py

Revision 9221, 33.5 kB (checked in by ikelly, 3 days ago)

Fixed .distinct() not working with slicing in Oracle, due to the
row numbers necessarily being distinct.

Line 
1 """
2 Various complex queries that have been problematic in the past.
3 """
4
5 import datetime
6 import pickle
7 import sys
8
9 from django.db import models
10 from django.db.models.query import Q, ITER_CHUNK_SIZE
11
12 # Python 2.3 doesn't have sorted()
13 try:
14     sorted
15 except NameError:
16     from django.utils.itercompat import sorted
17
18 class Tag(models.Model):
19     name = models.CharField(max_length=10)
20     parent = models.ForeignKey('self', blank=True, null=True,
21             related_name='children')
22
23     class Meta:
24         ordering = ['name']
25
26     def __unicode__(self):
27         return self.name
28
29 class Note(models.Model):
30     note = models.CharField(max_length=100)
31     misc = models.CharField(max_length=10)
32
33     class Meta:
34         ordering = ['note']
35
36     def __unicode__(self):
37         return self.note
38
39 class Annotation(models.Model):
40     name = models.CharField(max_length=10)
41     tag = models.ForeignKey(Tag)
42     notes = models.ManyToManyField(Note)
43
44     def __unicode__(self):
45         return self.name
46
47 class ExtraInfo(models.Model):
48     info = models.CharField(max_length=100)
49     note = models.ForeignKey(Note)
50
51     class Meta:
52         ordering = ['info']
53
54     def __unicode__(self):
55         return self.info
56
57 class Author(models.Model):
58     name = models.CharField(max_length=10)
59     num = models.IntegerField(unique=True)
60     extra = models.ForeignKey(ExtraInfo)
61
62     def __unicode__(self):
63         return self.name
64
65 class Item(models.Model):
66     name = models.CharField(max_length=10)
67     created = models.DateTimeField()
68     modified = models.DateTimeField(blank=True, null=True)
69     tags = models.ManyToManyField(Tag, blank=True, null=True)
70     creator = models.ForeignKey(Author)
71     note = models.ForeignKey(Note)
72
73     class Meta:
74         ordering = ['-note', 'name']
75
76     def __unicode__(self):
77         return self.name
78
79 class Report(models.Model):
80     name = models.CharField(max_length=10)
81     creator = models.ForeignKey(Author, to_field='num', null=True)
82
83     def __unicode__(self):
84         return self.name
85
86 class Ranking(models.Model):
87     rank = models.IntegerField()
88     author = models.ForeignKey(Author)
89
90     class Meta:
91         # A complex ordering specification. Should stress the system a bit.
92         ordering = ('author__extra__note', 'author__name', 'rank')
93
94     def __unicode__(self):
95         return '%d: %s' % (self.rank, self.author.name)
96
97 class Cover(models.Model):
98     title = models.CharField(max_length=50)
99     item = models.ForeignKey(Item)
100
101     class Meta:
102         ordering = ['item']
103
104     def __unicode__(self):
105         return self.title
106
107 class Number(models.Model):
108     num = models.IntegerField()
109
110     def __unicode__(self):
111         return unicode(self.num)
112
113 # Symmetrical m2m field with a normal field using the reverse accesor name
114 # ("valid").
115 class Valid(models.Model):
116     valid = models.CharField(max_length=10)
117     parent = models.ManyToManyField('self')
118
119     class Meta:
120         ordering = ['valid']
121
122 # Some funky cross-linked models for testing a couple of infinite recursion
123 # cases.
124 class X(models.Model):
125     y = models.ForeignKey('Y')
126
127 class Y(models.Model):
128     x1 = models.ForeignKey(X, related_name='y1')
129
130 # Some models with a cycle in the default ordering. This would be bad if we
131 # didn't catch the infinite loop.
132 class LoopX(models.Model):
133     y = models.ForeignKey('LoopY')
134
135     class Meta:
136         ordering = ['y']
137
138 class LoopY(models.Model):
139     x = models.ForeignKey(LoopX)
140
141     class Meta:
142         ordering = ['x']
143
144 class LoopZ(models.Model):
145     z = models.ForeignKey('self')
146
147     class Meta:
148         ordering = ['z']
149
150 # A model and custom default manager combination.
151 class CustomManager(models.Manager):
152     def get_query_set(self):
153         qs = super(CustomManager, self).get_query_set()
154         return qs.filter(public=True, tag__name='t1')
155
156 class ManagedModel(models.Model):
157     data = models.CharField(max_length=10)
158     tag = models.ForeignKey(Tag)
159     public = models.BooleanField(default=True)
160
161     objects = CustomManager()
162     normal_manager = models.Manager()
163
164     def __unicode__(self):
165         return self.data
166
167 # An inter-related setup with multiple paths from Child to Detail.
168 class Detail(models.Model):
169     data = models.CharField(max_length=10)
170
171 class MemberManager(models.Manager):
172     def get_query_set(self):
173         return super(MemberManager, self).get_query_set().select_related("details")
174
175 class Member(models.Model):
176     name = models.CharField(max_length=10)
177     details = models.OneToOneField(Detail, primary_key=True)
178
179     objects = MemberManager()
180
181 class Child(models.Model):
182     person = models.OneToOneField(Member, primary_key=True)
183     parent = models.ForeignKey(Member, related_name="children")
184
185 # Custom primary keys interfered with ordering in the past.
186 class CustomPk(models.Model):
187     name = models.CharField(max_length=10, primary_key=True)
188     extra = models.CharField(max_length=10)
189
190     class Meta:
191         ordering = ['name', 'extra']
192
193 class Related(models.Model):
194     custom = models.ForeignKey(CustomPk)
195
196 # An inter-related setup with a model subclass that has a nullable
197 # path to another model, and a return path from that model.
198
199 class Celebrity(models.Model):
200     name = models.CharField("Name", max_length=20)
201     greatest_fan = models.ForeignKey("Fan", null=True, unique=True)
202
203 class TvChef(Celebrity):
204     pass
205
206 class Fan(models.Model):
207     fan_of = models.ForeignKey(Celebrity)
208
209 # Multiple foreign keys
210 class LeafA(models.Model):
211     data = models.CharField(max_length=10)
212
213     def __unicode__(self):
214         return self.data
215
216 class LeafB(models.Model):
217     data = models.CharField(max_length=10)
218
219 class Join(models.Model):
220     a = models.ForeignKey(LeafA)
221     b = models.ForeignKey(LeafB)
222
223 class ReservedName(models.Model):
224     name = models.CharField(max_length=20)
225     order = models.IntegerField()
226
227     def __unicode__(self):
228         return self.name
229
230 __test__ = {'API_TESTS':"""
231 >>> t1 = Tag.objects.create(name='t1')
232 >>> t2 = Tag.objects.create(name='t2', parent=t1)
233 >>> t3 = Tag.objects.create(name='t3', parent=t1)
234 >>> t4 = Tag.objects.create(name='t4', parent=t3)
235 >>> t5 = Tag.objects.create(name='t5', parent=t3)
236
237 >>> n1 = Note.objects.create(note='n1', misc='foo')
238 >>> n2 = Note.objects.create(note='n2', misc='bar')
239 >>> n3 = Note.objects.create(note='n3', misc='foo')
240
241 >>> ann1 = Annotation.objects.create(name='a1', tag=t1)
242 >>> ann1.notes.add(n1)
243 >>> ann2 = Annotation.objects.create(name='a2', tag=t4)
244 >>> ann2.notes.add(n2, n3)
245
246 Create these out of order so that sorting by 'id' will be different to sorting
247 by 'info'. Helps detect some problems later.
248 >>> e2 = ExtraInfo.objects.create(info='e2', note=n2)
249 >>> e1 = ExtraInfo.objects.create(info='e1', note=n1)
250
251 >>> a1 = Author.objects.create(name='a1', num=1001, extra=e1)
252 >>> a2 = Author.objects.create(name='a2', num=2002, extra=e1)
253 >>> a3 = Author.objects.create(name='a3', num=3003, extra=e2)
254 >>> a4 = Author.objects.create(name='a4', num=4004, extra=e2)
255
256 >>> time1 = datetime.datetime(2007, 12, 19, 22, 25, 0)
257 >>> time2 = datetime.datetime(2007, 12, 19, 21, 0, 0)
258 >>> time3 = datetime.datetime(2007, 12, 20, 22, 25, 0)
259 >>> time4 = datetime.datetime(2007, 12, 20, 21, 0, 0)
260 >>> i1 = Item.objects.create(name='one', created=time1, modified=time1, creator=a1, note=n3)
261 >>> i1.tags = [t1, t2]
262 >>> i2 = Item.objects.create(name='two', created=time2, creator=a2, note=n2)
263 >>> i2.tags = [t1, t3]
264 >>> i3 = Item.objects.create(name='three', created=time3, creator=a2, note=n3)
265 >>> i4 = Item.objects.create(name='four', created=time4, creator=a4, note=n3)
266 >>> i4.tags = [t4]
267
268 >>> r1 = Report.objects.create(name='r1', creator=a1)
269 >>> r2 = Report.objects.create(name='r2', creator=a3)
270 >>> r3 = Report.objects.create(name='r3')
271
272 Ordering by 'rank' gives us rank2, rank1, rank3. Ordering by the Meta.ordering
273 will be rank3, rank2, rank1.
274 >>> rank1 = Ranking.objects.create(rank=2, author=a2)
275 >>> rank2 = Ranking.objects.create(rank=1, author=a3)
276 >>> rank3 = Ranking.objects.create(rank=3, author=a1)
277
278 >>> c1 = Cover.objects.create(title="first", item=i4)
279 >>> c2 = Cover.objects.create(title="second", item=i2)
280
281 >>> num1 = Number.objects.create(num=4)
282 >>> num2 = Number.objects.create(num=8)
283 >>> num3 = Number.objects.create(num=12)
284
285 Bug #1050
286 >>> Item.objects.filter(tags__isnull=True)
287 [<Item: three>]
288 >>> Item.objects.filter(tags__id__isnull=True)
289 [<Item: three>]
290
291 Bug #1801
292 >>> Author.objects.filter(item=i2)
293 [<Author: a2>]
294 >>> Author.objects.filter(item=i3)
295 [<Author: a2>]
296 >>> Author.objects.filter(item=i2) & Author.objects.filter(item=i3)
297 [<Author: a2>]
298
299 Bug #2306
300 Checking that no join types are "left outer" joins.
301 >>> query = Item.objects.filter(tags=t2).query
302 >>> query.LOUTER not in [x[2] for x in query.alias_map.values()]
303 True
304
305 >>> Item.objects.filter(Q(tags=t1)).order_by('name')
306 [<Item: one>, <Item: two>]
307 >>> Item.objects.filter(Q(tags=t1)).filter(Q(tags=t2))
308 [<Item: one>]
309 >>> Item.objects.filter(Q(tags=t1)).filter(Q(creator__name='fred')|Q(tags=t2))
310 [<Item: one>]
311
312 Each filter call is processed "at once" against a single table, so this is
313 different from the previous example as it tries to find tags that are two
314 things at once (rather than two tags).
315 >>> Item.objects.filter(Q(tags=t1) & Q(tags=t2))
316 []
317 >>> Item.objects.filter(Q(tags=t1), Q(creator__name='fred')|Q(tags=t2))
318 []
319
320 >>> qs = Author.objects.filter(ranking__rank=2, ranking__id=rank1.id)
321 >>> list(qs)
322 [<Author: a2>]
323 >>> qs.query.count_active_tables()
324 2
325 >>> qs = Author.objects.filter(ranking__rank=2).filter(ranking__id=rank1.id)
326 >>> qs.query.count_active_tables()
327 3
328
329 Bug #4464
330 >>> Item.objects.filter(tags=t1).filter(tags=t2)
331 [<Item: one>]
332 >>> Item.objects.filter(tags__in=[t1, t2]).distinct().order_by('name')
333 [<Item: one>, <Item: two>]
334 >>> Item.objects.filter(tags__in=[t1, t2]).filter(tags=t3)
335 [<Item: two>]
336
337 Make sure .distinct() works with slicing (this was broken in Oracle).
338 >>> Item.objects.filter(tags__in=[t1, t2]).order_by('name')[:3]
339 [<Item: one>, <Item: one>, <Item: two>]
340 >>> Item.objects.filter(tags__in=[t1, t2]).distinct().order_by('name')[:3]
341 [<Item: one>, <Item: two>]
342
343 Bug #2080, #3592
344 >>> Author.objects.filter(item__name='one') | Author.objects.filter(name='a3')
345 [<Author: a1>, <Author: a3>]
346 >>> Author.objects.filter(Q(item__name='one') | Q(name='a3'))
347 [<Author: a1>, <Author: a3>]
348 >>> Author.objects.filter(Q(name='a3') | Q(item__name='one'))
349 [<Author: a1>, <Author: a3>]
350 >>> Author.objects.filter(Q(item__name='three') | Q(report__name='r3'))
351 [<Author: a2>]
352
353 Bug #4289
354 A slight variation on the above theme: restricting the choices by the lookup
355 constraints.
356 >>> Number.objects.filter(num__lt=4)
357 []
358 >>> Number.objects.filter(num__gt=8, num__lt=12)
359 []
360 >>> Number.objects.filter(num__gt=8, num__lt=13)
361 [<Number: 12>]
362 >>> Number.objects.filter(Q(num__lt=4) | Q(num__gt=8, num__lt=12))
363 []
364 >>> Number.objects.filter(Q(num__gt=8, num__lt=12) | Q(num__lt=4))
365 []
366 >>> Number.objects.filter(Q(num__gt=8) & Q(num__lt=12) | Q(num__lt=4))
367 []
368 >>> Number.objects.filter(Q(num__gt=7) & Q(num__lt=12) | Q(num__lt=4))
369 [<Number: 8>]
370
371 Bug #7872
372 Another variation on the disjunctive filtering theme.
373
374 # For the purposes of this regression test, it's important that there is no
375 # Join object releated to the LeafA we create.
376 >>> LeafA.objects.create(data='first')
377 <LeafA: first>
378 >>> LeafA.objects.filter(Q(data='first')|Q(join__b__data='second'))
379 [<LeafA: first>]
380
381 Bug #6074
382 Merging two empty result sets shouldn't leave a queryset with no constraints
383 (which would match everything).
384 >>> Author.objects.filter(Q(id__in=[]))
385 []
386 >>> Author.objects.filter(Q(id__in=[])|Q(id__in=[]))
387 []
388
389 Bug #1878, #2939
390 >>> Item.objects.values('creator').distinct().count()
391 3
392
393 # Create something with a duplicate 'name' so that we can test multi-column
394 # cases (which require some tricky SQL transformations under the covers).
395 >>> xx = Item(name='four', created=time1, creator=a2, note=n1)
396 >>> xx.save()
397 >>> Item.objects.exclude(name='two').values('creator', 'name').distinct().count()
398 4
399 >>> Item.objects.exclude(name='two').extra(select={'foo': '%s'}, select_params=(1,)).values('creator', 'name', 'foo').distinct().count()
400 4
401 >>> Item.objects.exclude(name='two').extra(select={'foo': '%s'}, select_params=(1,)).values('creator', 'name').distinct().count()
402 4
403 >>> xx.delete()
404
405 Bug #7323
406 >>> Item.objects.values('creator', 'name').count()
407 4
408
409 Bug #2253
410 >>> q1 = Item.objects.order_by('name')
411 >>> q2 = Item.objects.filter(id=i1.id)
412 >>> q1
413 [<Item: four>, <Item: one>, <Item: three>, <Item: two>]
414 >>> q2
415 [<Item: one>]
416 >>> (q1 | q2).order_by('name')
417 [<Item: four>, <Item: one>, <Item: three>, <Item: two>]
418 >>> (q1 & q2).order_by('name')
419 [<Item: one>]
420
421 # FIXME: This is difficult to fix and very much an edge case, so punt for now.
422 # # This is related to the order_by() tests, below, but the old bug exhibited
423 # # itself here (q2 was pulling too many tables into the combined query with the
424 # # new ordering, but only because we have evaluated q2 already).
425 # >>> len((q1 & q2).order_by('name').query.tables)
426 # 1
427
428 >>> q1 = Item.objects.filter(tags=t1)
429 >>> q2 = Item.objects.filter(note=n3, tags=t2)
430 >>> q3 = Item.objects.filter(creator=a4)
431 >>> ((q1 & q2) | q3).order_by('name')
432 [<Item: four>, <Item: one>]
433
434 Bugs #4088, #4306
435 >>> Report.objects.filter(creator=1001)
436 [<Report: r1>]
437 >>> Report.objects.filter(creator__num=1001)
438 [<Report: r1>]
439 >>> Report.objects.filter(creator__id=1001)
440 []
441 >>> Report.objects.filter(creator__id=a1.id)
442 [<Report: r1>]
443 >>> Report.objects.filter(creator__name='a1')
444 [<Report: r1>]
445
446 Bug #4510
447 >>> Author.objects.filter(report__name='r1')
448 [<Author: a1>]
449
450 Bug #7378
451 >>> a1.report_set.all()
452 [<Report: r1>]
453
454 Bug #5324, #6704
455 >>> Item.objects.filter(tags__name='t4')
456 [<Item: four>]
457 >>> Item.objects.exclude(tags__name='t4').order_by('name').distinct()
458 [<Item: one>, <Item: three>, <Item: two>]
459 >>> Item.objects.exclude(tags__name='t4').order_by('name').distinct().reverse()
460 [<Item: two>, <Item: three>, <Item: one>]
461 >>> Author.objects.exclude(item__name='one').distinct().order_by('name')
462 [<Author: a2>, <Author: a3>, <Author: a4>]
463
464
465 # Excluding across a m2m relation when there is more than one related object
466 # associated was problematic.
467 >>> Item.objects.exclude(tags__name='t1').order_by('name')
468 [<Item: four>, <Item: three>]
469 >>> Item.objects.exclude(tags__name='t1').exclude(tags__name='t4')
470 [<Item: three>]
471
472 # Excluding from a relation that cannot be NULL should not use outer joins.
473 >>> query = Item.objects.exclude(creator__in=[a1, a2]).query
474 >>> query.LOUTER not in [x[2] for x in query.alias_map.values()]
475 True
476
477 Similarly, when one of the joins cannot possibly, ever, involve NULL values (Author -> ExtraInfo, in the following), it should never be promoted to a left outer join. So the following query should only involve one "left outer" join (Author -> Item is 0-to-many).
478 >>> qs = Author.objects.filter(id=a1.id).filter(Q(extra__note=n1)|Q(item__note=n3))
479 >>> len([x[2] for x in qs.query.alias_map.values() if x[2] == query.LOUTER and qs.query.alias_refcount[x[1]]])
480 1
481
482 The previous changes shouldn't affect nullable foreign key joins.
483 >>> Tag.objects.filter(parent__isnull=True).order_by('name')
484 [<Tag: t1>]
485 >>> Tag.objects.exclude(parent__isnull=True).order_by('name')
486 [<Tag: t2>, <Tag: t3>, <Tag: t4>, <Tag: t5>]
487 >>> Tag.objects.exclude(Q(parent__name='t1') | Q(parent__isnull=True)).order_by('name')
488 [<Tag: t4>, <Tag: t5>]
489 >>> Tag.objects.exclude(Q(parent__isnull=True) | Q(parent__name='t1')).order_by('name')
490 [<Tag: t4>, <Tag: t5>]
491 >>> Tag.objects.exclude(Q(parent__parent__isnull=True)).order_by('name')
492 [<Tag: t4>, <Tag: t5>]
493 >>> Tag.objects.filter(~Q(parent__parent__isnull=True)).order_by('name')
494 [<Tag: t4>, <Tag: t5>]
495
496 Bug #2091
497 >>> t = Tag.objects.get(name='t4')
498 >>> Item.objects.filter(tags__in=[t])
499 [<Item: four>]
500
501 Combining querysets built on different models should behave in a well-defined
502 fashion. We raise an error.
503 >>> Author.objects.all() & Tag.objects.all()
504 Traceback (most recent call last):
505 ...
506 AssertionError: Cannot combine queries on two different base models.
507 >>> Author.objects.all() | Tag.objects.all()
508 Traceback (most recent call last):
509 ...
510 AssertionError: Cannot combine queries on two different base models.
511
512 Bug #3141
513 >>> Author.objects.extra(select={'foo': '1'}).count()
514 4
515 >>> Author.objects.extra(select={'foo': '%s'}, select_params=(1,)).count()
516 4
517
518 Bug #2400
519 >>> Author.objects.filter(item__isnull=True)
520 [<Author: a3>]
521 >>> Tag.objects.filter(item__isnull=True)
522 [<Tag: t5>]
523
524 Bug #2496
525 >>> Item.objects.extra(tables=['queries_author']).select_related().order_by('name')[:1]
526 [<Item: four>]
527
528 Bug #2076
529 # Ordering on related tables should be possible, even if the table is not
530 # otherwise involved.
531 >>> Item.objects.order_by('note__note', 'name')
532 [<Item: two>, <Item: four>, <Item: one>, <Item: three>]
533
534 # Ordering on a related field should use the remote model's default ordering as
535 # a final step.
536 >>> Author.objects.order_by('extra', '-name')
537 [<Author: a2>, <Author: a1>, <Author: a4>, <Author: a3>]
538
539 # Using remote model default ordering can span multiple models (in this case,
540 # Cover is ordered by Item's default, which uses Note's default).
541 >>> Cover.objects.all()
542 [<Cover: first>, <Cover: second>]
543
544 # If the remote model does not have a default ordering, we order by its 'id'
545 # field.
546 >>> Item.objects.order_by('creator', 'name')
547 [<Item: one>, <Item: three>, <Item: two>, <Item: four>]
548
549 # Cross model ordering is possible in Meta, too.
550 >>> Ranking.objects.all()
551 [<Ranking: 3: a1>, <Ranking: 2: a2>, <Ranking: 1: a3>]
552 >>> Ranking.objects.all().order_by('rank')
553 [<Ranking: 1: a3>, <Ranking: 2: a2>, <Ranking: 3: a1>]
554
555 # Ordering by a many-valued attribute (e.g. a many-to-many or reverse
556 # ForeignKey) is legal, but the results might not make sense. That isn't
557 # Django's problem. Garbage in, garbage out.
558 >>> Item.objects.filter(tags__isnull=False).order_by('tags', 'id')
559 [<Item: one>, <Item: two>, <Item: one>, <Item: two>, <Item: four>]
560
561 # If we replace the default ordering, Django adjusts the required tables
562 # automatically. Item normally requires a join with Note to do the default
563 # ordering, but that isn't needed here.
564 >>> qs = Item.objects.order_by('name')
565 >>> list(qs)
566 [<Item: four>, <Item: one>, <Item: three>, <Item: two>]
567 >>> len(qs.query.tables)
568 1
569
570 # Ordering of extra() pieces is possible, too and you can mix extra fields and
571 # model fields in the ordering.
572 >>> Ranking.objects.extra(tables=['django_site'], order_by=['-django_site.id', 'rank'])
573 [<Ranking: 1: a3>, <Ranking: 2: a2>, <Ranking: 3: a1>]
574
575 >>> qs = Ranking.objects.extra(select={'good': 'case when rank > 2 then 1 else 0 end'})
576 >>> [o.good for o in qs.extra(order_by=('-good',))] == [True, False, False]
577 True
578 >>> qs.extra(order_by=('-good', 'id'))
579 [<Ranking: 3: a1>, <Ranking: 2: a2>, <Ranking: 1: a3>]
580
581 # Despite having some extra aliases in the query, we can still omit them in a
582 # values() query.
583 >>> dicts = qs.values('id', 'rank').order_by('id')
584 >>> [sorted(d.items()) for d in dicts]
585 [[('id', 1), ('rank', 2)], [('id', 2), ('rank', 1)], [('id', 3), ('rank', 3)]]
586
587 Bug #7256
588 # An empty values() call includes all aliases, including those from an extra()
589 >>> dicts = qs.values().order_by('id')
590 >>> [sorted(d.items()) for d in dicts]
591 [[('author_id', 2), ('good', 0), ('id', 1), ('rank', 2)], [('author_id', 3), ('good', 0), ('id', 2), ('rank', 1)], [('author_id', 1), ('good', 1), ('id', 3), ('rank', 3)]]
592
593 Bugs #2874, #3002
594 >>> qs = Item.objects.select_related().order_by('note__note', 'name')
595 >>> list(qs)
596 [<Item: two>, <Item: four>, <Item: one>, <Item: three>]
597
598 # This is also a good select_related() test because there are multiple Note
599 # entries in the SQL. The two Note items should be different.
600 >>> qs[0].note, qs[0].creator.extra.note
601 (<Note: n2>, <Note: n1>)
602
603 Bug #3037
604 >>> Item.objects.filter(Q(creator__name='a3', name='two')|Q(creator__name='a4', name='four'))
605 [<Item: four>]
606
607 Bug #5321, #7070
608
609 Ordering columns must be included in the output columns. Note that this means
610 results that might otherwise be distinct are not (if there are multiple values
611 in the ordering cols), as in this example. This isn't a bug; it's a warning to
612 be careful with the selection of ordering columns.
613
614 >>> Note.objects.values('misc').distinct().order_by('note', '-misc')
615 [{'misc': u'foo'}, {'misc': u'bar'}, {'misc': u'foo'}]
616
617 Bug #4358
618 If you don't pass any fields to values(), relation fields are returned as
619 "foo_id" keys, not "foo". For consistency, you should be able to pass "foo_id"
620 in the fields list and have it work, too. We actually allow both "foo" and
621 "foo_id".
622
623 # The *_id version is returned by default.
624 >>> 'note_id' in ExtraInfo.objects.values()[0]
625 True
626
627 # You can also pass it in explicitly.
628 >>> ExtraInfo.objects.values('note_id')
629 [{'note_id': 1}, {'note_id': 2}]
630
631 # ...or use the field name.
632 >>> ExtraInfo.objects.values('note')
633 [{'note': 1}, {'note': 2}]
634
635 Bug #5261
636 >>> Note.objects.exclude(Q())
637 [<Note: n1>, <Note: n2>, <Note: n3>]
638
639 Bug #3045, #3288
640 Once upon a time, select_related() with circular relations would loop
641 infinitely if you forgot to specify "depth". Now we set an arbitrary default
642 upper bound.
643 >>> X.objects.all()
644 []
645 >>> X.objects.select_related()
646 []
647
648 Bug #3739
649 The all() method on querysets returns a copy of the queryset.
650 >>> q1 = Item.objects.order_by('name')
651 >>> id(q1) == id(q1.all())
652 False
653
654 Bug #2902
655 Parameters can be given to extra_select, *if* you use a SortedDict.
656
657 (First we need to know which order the keys fall in "naturally" on your system,
658 so we can put things in the wrong way around from normal. A normal dict would
659 thus fail.)
660 >>> from django.utils.datastructures import SortedDict
661 >>> s = [('a', '%s'), ('b', '%s')]
662 >>> params = ['one', 'two']
663 >>> if {'a': 1, 'b': 2}.keys() == ['a', 'b']:
664 ...     s.reverse()
665 ...     params.reverse()
666
667 # This slightly odd comparison works aorund the fact that PostgreSQL will
668 # return 'one' and 'two' as strings, not Unicode objects. It's a side-effect of
669 # using constants here and not a real concern.
670 >>> d = Item.objects.extra(select=SortedDict(s), select_params=params).values('a', 'b')[0]
671 >>> d == {'a': u'one', 'b': u'two'}
672 True
673
674 # Order by the number of tags attached to an item.
675 >>> l = Item.objects.extra(select={'count': 'select count(*) from queries_item_tags where queries_item_tags.item_id = queries_item.id'}).order_by('-count')
676 >>> [o.count for o in l]
677 [2, 2, 1, 0]
678
679 Bug #6154
680 Multiple filter statements are joined using "AND" all the time.
681
682 >>> Author.objects.filter(id=a1.id).filter(Q(extra__note=n1)|Q(item__note=n3))
683 [<Author: a1>]
684 >>> Author.objects.filter(Q(extra__note=n1)|Q(item__note=n3)).filter(id=a1.id)
685 [<Author: a1>]
686
687 Bug #6981
688 >>> Tag.objects.select_related('parent').order_by('name')
689 [<Tag: t1>, <Tag: t2>, <Tag: t3>, <Tag: t4>, <Tag: t5>]
690
691 Bug #6180, #6203 -- dates with limits and/or counts
692 >>> Item.objects.count()
693 4
694 >>> Item.objects.dates('created', 'month').count()
695 1
696 >>> Item.objects.dates('created', 'day').count()
697 2
698 >>> len(Item.objects.dates('created', 'day'))
699 2
700 >>> Item.objects.dates('created', 'day')[0]
701 datetime.datetime(2007, 12, 19, 0, 0)
702
703 Bug #7087 -- dates with extra select columns
704 >>> Item.objects.dates('created', 'day').extra(select={'a': 1})
705 [datetime.datetime(2007, 12, 19, 0, 0), datetime.datetime(2007, 12, 20, 0, 0)]
706
707 Bug #7155 -- nullable dates
708 >>> Item.objects.dates('modified', 'day')
709 [datetime.datetime(2007, 12, 19, 0, 0)]
710
711 Test that parallel iterators work.
712
713 >>> qs = Tag.objects.all()
714 >>> i1, i2 = iter(qs), iter(qs)
715 >>> i1.next(), i1.next()
716 (<Tag: t1>, <Tag: t2>)
717 >>> i2.next(), i2.next(), i2.next()
718 (<Tag: t1>, <Tag: t2>, <Tag: t3>)
719 >>> i1.next()
720 <Tag: t3>
721
722 >>> qs = X.objects.all()
723 >>> bool(qs)
724 False
725 >>> bool(qs)
726 False
727
728 We can do slicing beyond what is currently in the result cache, too.
729
730 ## FIXME!! This next test causes really weird PostgreSQL behaviour, but it's
731 ## only apparent much later when the full test suite runs. I don't understand
732 ## what's going on here yet.
733 ##
734 ## # We need to mess with the implemenation internals a bit here to decrease the
735 ## # cache fill size so that we don't read all the results at once.
736 ## >>> from django.db.models import query
737 ## >>> query.ITER_CHUNK_SIZE = 2
738 ## >>> qs = Tag.objects.all()
739 ##
740 ## # Fill the cache with the first chunk.
741 ## >>> bool(qs)
742 ## True
743 ## >>> len(qs._result_cache)
744 ## 2
745 ##
746 ## # Query beyond the end of the cache and check that it is filled out as required.
747 ## >>> qs[4]
748 ## <Tag: t5>
749 ## >>> len(qs._result_cache)
750 ## 5
751 ##
752 ## # But querying beyond the end of the result set will fail.
753 ## >>> qs[100]
754 ## Traceback (most recent call last):
755 ## ...
756 ## IndexError: ...
757
758 Bug #7045 -- extra tables used to crash SQL construction on the second use.
759 >>> qs = Ranking.objects.extra(tables=['django_site'])
760 >>> s = qs.query.as_sql()
761 >>> s = qs.query.as_sql()   # test passes if this doesn't raise an exception.
762
763 Bug #7098 -- Make sure semi-deprecated ordering by related models syntax still
764 works.
765 >>> Item.objects.values('note__note').order_by('queries_note.note', 'id')
766 [{'note__note': u'n2'}, {'note__note': u'n3'}, {'note__note': u'n3'}, {'note__note': u'n3'}]
767
768 Bug #7096 -- Make sure exclude() with multiple conditions continues to work.
769 >>> Tag.objects.filter(parent=t1, name='t3').order_by('name')
770 [<Tag: t3>]
771 >>> Tag.objects.exclude(parent=t1, name='t3').order_by('name')
772 [<Tag: t1>, <Tag: t2>, <Tag: t4>, <Tag: t5>]
773 >>> Item.objects.exclude(tags__name='t1', name='one').order_by('name').distinct()
774 [<Item: four>, <Item: three>, <Item: two>]
775 >>> Item.objects.filter(name__in=['three', 'four']).exclude(tags__name='t1').order_by('name')
776 [<Item: four>, <Item: three>]
777
778 More twisted cases, involving nested negations.
779 >>> Item.objects.exclude(~Q(tags__name='t1', name='one'))
780 [<Item: one>]
781 >>> Item.objects.filter(~Q(tags__name='t1', name='one'), name='two')
782 [<Item: two>]
783 >>> Item.objects.exclude(~Q(tags__name='t1', name='one'), name='two')
784 [<Item: four>, <Item: one>, <Item: three>]
785
786 Bug #7095
787 Updates that are filtered on the model being updated are somewhat tricky to get
788 in MySQL. This exercises that case.
789 >>> mm = ManagedModel.objects.create(data='mm1', tag=t1, public=True)
790 >>> ManagedModel.objects.update(data='mm')
791 1
792
793 A values() or values_list() query across joined models must use outer joins
794 appropriately.
795 >>> Report.objects.values_list("creator__extra__info", flat=True).order_by("name")
796 [u'e1', u'e2', None]
797
798 Similarly for select_related(), joins beyond an initial nullable join must
799 use outer joins so that all results are included.
800 >>> Report.objects.select_related("creator", "creator__extra").order_by("name")
801 [<Report: r1>, <Report: r2>, <Report: r3>]
802
803 When there are multiple paths to a table from another table, we have to be
804 careful not to accidentally reuse an inappropriate join when using
805 select_related(). We used to return the parent's Detail record here by mistake.
806
807 >>> d1 = Detail.objects.create(data="d1")
808 >>> d2 = Detail.objects.create(data="d2")
809 >>> m1 = Member.objects.create(name="m1", details=d1)
810 >>> m2 = Member.objects.create(name="m2", details=d2)
811 >>> c1 = Child.objects.create(person=m2, parent=m1)
812 >>> obj = m1.children.select_related("person__details")[0]
813 >>> obj.person.details.data
814 u'd2'
815
816 Bug #7076 -- excluding shouldn't eliminate NULL entries.
817 >>> Item.objects.exclude(modified=time1).order_by('name')
818 [<Item: four>, <Item: three>, <Item: two>]
819 >>> Tag.objects.exclude(parent__name=t1.name)
820 [<Tag: t1>, <Tag: t4>, <Tag: t5>]
821
822 Bug #7181 -- ordering by related tables should accomodate nullable fields (this
823 test is a little tri