test_qs_combinators.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. from django.db.models import F, IntegerField, Value
  2. from django.db.utils import DatabaseError
  3. from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
  4. from .models import Number, ReservedName
  5. @skipUnlessDBFeature('supports_select_union')
  6. class QuerySetSetOperationTests(TestCase):
  7. @classmethod
  8. def setUpTestData(cls):
  9. Number.objects.bulk_create(Number(num=i) for i in range(10))
  10. def number_transform(self, value):
  11. return value.num
  12. def assertNumbersEqual(self, queryset, expected_numbers, ordered=True):
  13. self.assertQuerysetEqual(queryset, expected_numbers, self.number_transform, ordered)
  14. def test_simple_union(self):
  15. qs1 = Number.objects.filter(num__lte=1)
  16. qs2 = Number.objects.filter(num__gte=8)
  17. qs3 = Number.objects.filter(num=5)
  18. self.assertNumbersEqual(qs1.union(qs2, qs3), [0, 1, 5, 8, 9], ordered=False)
  19. @skipUnlessDBFeature('supports_select_intersection')
  20. def test_simple_intersection(self):
  21. qs1 = Number.objects.filter(num__lte=5)
  22. qs2 = Number.objects.filter(num__gte=5)
  23. qs3 = Number.objects.filter(num__gte=4, num__lte=6)
  24. self.assertNumbersEqual(qs1.intersection(qs2, qs3), [5], ordered=False)
  25. @skipUnlessDBFeature('supports_select_difference')
  26. def test_simple_difference(self):
  27. qs1 = Number.objects.filter(num__lte=5)
  28. qs2 = Number.objects.filter(num__lte=4)
  29. self.assertNumbersEqual(qs1.difference(qs2), [5], ordered=False)
  30. def test_union_distinct(self):
  31. qs1 = Number.objects.all()
  32. qs2 = Number.objects.all()
  33. self.assertEqual(len(list(qs1.union(qs2, all=True))), 20)
  34. self.assertEqual(len(list(qs1.union(qs2))), 10)
  35. def test_union_bad_kwarg(self):
  36. qs1 = Number.objects.all()
  37. msg = "union() received an unexpected keyword argument 'bad'"
  38. with self.assertRaisesMessage(TypeError, msg):
  39. self.assertEqual(len(list(qs1.union(qs1, bad=True))), 20)
  40. def test_limits(self):
  41. qs1 = Number.objects.all()
  42. qs2 = Number.objects.all()
  43. self.assertEqual(len(list(qs1.union(qs2)[:2])), 2)
  44. def test_ordering(self):
  45. qs1 = Number.objects.filter(num__lte=1)
  46. qs2 = Number.objects.filter(num__gte=2, num__lte=3)
  47. self.assertNumbersEqual(qs1.union(qs2).order_by('-num'), [3, 2, 1, 0])
  48. @skipUnlessDBFeature('supports_slicing_ordering_in_compound')
  49. def test_ordering_subqueries(self):
  50. qs1 = Number.objects.order_by('num')[:2]
  51. qs2 = Number.objects.order_by('-num')[:2]
  52. self.assertNumbersEqual(qs1.union(qs2).order_by('-num')[:4], [9, 8, 1, 0])
  53. @skipIfDBFeature('supports_slicing_ordering_in_compound')
  54. def test_unsupported_ordering_slicing_raises_db_error(self):
  55. qs1 = Number.objects.all()
  56. qs2 = Number.objects.all()
  57. msg = 'LIMIT/OFFSET not allowed in subqueries of compound statements'
  58. with self.assertRaisesMessage(DatabaseError, msg):
  59. list(qs1.union(qs2[:10]))
  60. msg = 'ORDER BY not allowed in subqueries of compound statements'
  61. with self.assertRaisesMessage(DatabaseError, msg):
  62. list(qs1.order_by('id').union(qs2))
  63. @skipIfDBFeature('supports_select_intersection')
  64. def test_unsupported_intersection_raises_db_error(self):
  65. qs1 = Number.objects.all()
  66. qs2 = Number.objects.all()
  67. msg = 'intersection not supported on this database backend'
  68. with self.assertRaisesMessage(DatabaseError, msg):
  69. list(qs1.intersection(qs2))
  70. def test_combining_multiple_models(self):
  71. ReservedName.objects.create(name='99 little bugs', order=99)
  72. qs1 = Number.objects.filter(num=1).values_list('num', flat=True)
  73. qs2 = ReservedName.objects.values_list('order')
  74. self.assertEqual(list(qs1.union(qs2).order_by('num')), [1, 99])
  75. def test_order_raises_on_non_selected_column(self):
  76. qs1 = Number.objects.filter().annotate(
  77. annotation=Value(1, IntegerField()),
  78. ).values('annotation', num2=F('num'))
  79. qs2 = Number.objects.filter().values('id', 'num')
  80. # Should not raise
  81. list(qs1.union(qs2).order_by('annotation'))
  82. list(qs1.union(qs2).order_by('num2'))
  83. msg = 'ORDER BY term does not match any column in the result set'
  84. # 'id' is not part of the select
  85. with self.assertRaisesMessage(DatabaseError, msg):
  86. list(qs1.union(qs2).order_by('id'))
  87. # 'num' got realiased to num2
  88. with self.assertRaisesMessage(DatabaseError, msg):
  89. list(qs1.union(qs2).order_by('num'))
  90. # switched order, now 'exists' again:
  91. list(qs2.union(qs1).order_by('num'))