tests.py 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  1. import datetime
  2. from decimal import Decimal
  3. from django.core.exceptions import FieldDoesNotExist, FieldError
  4. from django.db import connection
  5. from django.db.models import (
  6. BooleanField, Case, CharField, Count, DateTimeField, DecimalField, Exists,
  7. ExpressionWrapper, F, FloatField, Func, IntegerField, Max,
  8. NullBooleanField, OuterRef, Q, Subquery, Sum, Value, When,
  9. )
  10. from django.db.models.expressions import RawSQL
  11. from django.db.models.functions import (
  12. Coalesce, ExtractYear, Floor, Length, Lower, Trim,
  13. )
  14. from django.test import TestCase, skipUnlessDBFeature
  15. from django.test.utils import register_lookup
  16. from .models import (
  17. Author, Book, Company, DepartmentStore, Employee, Publisher, Store, Ticket,
  18. )
  19. def cxOracle_py3_bug(func):
  20. """
  21. There's a bug in Django/cx_Oracle with respect to string handling under
  22. Python 3 (essentially, they treat Python 3 strings as Python 2 strings
  23. rather than unicode). This makes some tests here fail under Python 3, so
  24. we mark them as expected failures until someone fixes them in #23843.
  25. """
  26. from unittest import expectedFailure
  27. from django.db import connection
  28. return expectedFailure(func) if connection.vendor == 'oracle' else func
  29. class NonAggregateAnnotationTestCase(TestCase):
  30. @classmethod
  31. def setUpTestData(cls):
  32. cls.a1 = Author.objects.create(name='Adrian Holovaty', age=34)
  33. cls.a2 = Author.objects.create(name='Jacob Kaplan-Moss', age=35)
  34. cls.a3 = Author.objects.create(name='Brad Dayley', age=45)
  35. cls.a4 = Author.objects.create(name='James Bennett', age=29)
  36. cls.a5 = Author.objects.create(name='Jeffrey Forcier', age=37)
  37. cls.a6 = Author.objects.create(name='Paul Bissex', age=29)
  38. cls.a7 = Author.objects.create(name='Wesley J. Chun', age=25)
  39. cls.a8 = Author.objects.create(name='Peter Norvig', age=57)
  40. cls.a9 = Author.objects.create(name='Stuart Russell', age=46)
  41. cls.a1.friends.add(cls.a2, cls.a4)
  42. cls.a2.friends.add(cls.a1, cls.a7)
  43. cls.a4.friends.add(cls.a1)
  44. cls.a5.friends.add(cls.a6, cls.a7)
  45. cls.a6.friends.add(cls.a5, cls.a7)
  46. cls.a7.friends.add(cls.a2, cls.a5, cls.a6)
  47. cls.a8.friends.add(cls.a9)
  48. cls.a9.friends.add(cls.a8)
  49. cls.p1 = Publisher.objects.create(name='Apress', num_awards=3)
  50. cls.p2 = Publisher.objects.create(name='Sams', num_awards=1)
  51. cls.p3 = Publisher.objects.create(name='Prentice Hall', num_awards=7)
  52. cls.p4 = Publisher.objects.create(name='Morgan Kaufmann', num_awards=9)
  53. cls.p5 = Publisher.objects.create(name="Jonno's House of Books", num_awards=0)
  54. cls.b1 = Book.objects.create(
  55. isbn='159059725', name='The Definitive Guide to Django: Web Development Done Right',
  56. pages=447, rating=4.5, price=Decimal('30.00'), contact=cls.a1, publisher=cls.p1,
  57. pubdate=datetime.date(2007, 12, 6)
  58. )
  59. cls.b2 = Book.objects.create(
  60. isbn='067232959', name='Sams Teach Yourself Django in 24 Hours',
  61. pages=528, rating=3.0, price=Decimal('23.09'), contact=cls.a3, publisher=cls.p2,
  62. pubdate=datetime.date(2008, 3, 3)
  63. )
  64. cls.b3 = Book.objects.create(
  65. isbn='159059996', name='Practical Django Projects',
  66. pages=300, rating=4.0, price=Decimal('29.69'), contact=cls.a4, publisher=cls.p1,
  67. pubdate=datetime.date(2008, 6, 23)
  68. )
  69. cls.b4 = Book.objects.create(
  70. isbn='013235613', name='Python Web Development with Django',
  71. pages=350, rating=4.0, price=Decimal('29.69'), contact=cls.a5, publisher=cls.p3,
  72. pubdate=datetime.date(2008, 11, 3)
  73. )
  74. cls.b5 = Book.objects.create(
  75. isbn='013790395', name='Artificial Intelligence: A Modern Approach',
  76. pages=1132, rating=4.0, price=Decimal('82.80'), contact=cls.a8, publisher=cls.p3,
  77. pubdate=datetime.date(1995, 1, 15)
  78. )
  79. cls.b6 = Book.objects.create(
  80. isbn='155860191', name='Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp',
  81. pages=946, rating=5.0, price=Decimal('75.00'), contact=cls.a8, publisher=cls.p4,
  82. pubdate=datetime.date(1991, 10, 15)
  83. )
  84. cls.b1.authors.add(cls.a1, cls.a2)
  85. cls.b2.authors.add(cls.a3)
  86. cls.b3.authors.add(cls.a4)
  87. cls.b4.authors.add(cls.a5, cls.a6, cls.a7)
  88. cls.b5.authors.add(cls.a8, cls.a9)
  89. cls.b6.authors.add(cls.a8)
  90. cls.s1 = Store.objects.create(
  91. name='Amazon.com',
  92. original_opening=datetime.datetime(1994, 4, 23, 9, 17, 42),
  93. friday_night_closing=datetime.time(23, 59, 59)
  94. )
  95. cls.s2 = Store.objects.create(
  96. name='Books.com',
  97. original_opening=datetime.datetime(2001, 3, 15, 11, 23, 37),
  98. friday_night_closing=datetime.time(23, 59, 59)
  99. )
  100. cls.s3 = Store.objects.create(
  101. name="Mamma and Pappa's Books",
  102. original_opening=datetime.datetime(1945, 4, 25, 16, 24, 14),
  103. friday_night_closing=datetime.time(21, 30)
  104. )
  105. cls.s1.books.add(cls.b1, cls.b2, cls.b3, cls.b4, cls.b5, cls.b6)
  106. cls.s2.books.add(cls.b1, cls.b3, cls.b5, cls.b6)
  107. cls.s3.books.add(cls.b3, cls.b4, cls.b6)
  108. def test_basic_annotation(self):
  109. books = Book.objects.annotate(is_book=Value(1))
  110. for book in books:
  111. self.assertEqual(book.is_book, 1)
  112. def test_basic_f_annotation(self):
  113. books = Book.objects.annotate(another_rating=F('rating'))
  114. for book in books:
  115. self.assertEqual(book.another_rating, book.rating)
  116. def test_joined_annotation(self):
  117. books = Book.objects.select_related('publisher').annotate(
  118. num_awards=F('publisher__num_awards'))
  119. for book in books:
  120. self.assertEqual(book.num_awards, book.publisher.num_awards)
  121. def test_joined_transformed_annotation(self):
  122. Employee.objects.bulk_create([
  123. Employee(
  124. first_name='John',
  125. last_name='Doe',
  126. age=18,
  127. store=self.s1,
  128. salary=15000,
  129. ),
  130. Employee(
  131. first_name='Jane',
  132. last_name='Jones',
  133. age=30,
  134. store=self.s2,
  135. salary=30000,
  136. ),
  137. Employee(
  138. first_name='Jo',
  139. last_name='Smith',
  140. age=55,
  141. store=self.s3,
  142. salary=50000,
  143. ),
  144. ])
  145. employees = Employee.objects.annotate(
  146. store_opened_year=F('store__original_opening__year'),
  147. )
  148. for employee in employees:
  149. self.assertEqual(
  150. employee.store_opened_year,
  151. employee.store.original_opening.year,
  152. )
  153. def test_custom_transform_annotation(self):
  154. with register_lookup(DecimalField, Floor):
  155. books = Book.objects.annotate(floor_price=F('price__floor'))
  156. self.assertSequenceEqual(books.values_list('pk', 'floor_price'), [
  157. (self.b1.pk, 30),
  158. (self.b2.pk, 23),
  159. (self.b3.pk, 29),
  160. (self.b4.pk, 29),
  161. (self.b5.pk, 82),
  162. (self.b6.pk, 75),
  163. ])
  164. def test_chaining_transforms(self):
  165. Company.objects.create(name=' Django Software Foundation ')
  166. Company.objects.create(name='Yahoo')
  167. with register_lookup(CharField, Trim), register_lookup(CharField, Length):
  168. for expr in [Length('name__trim'), F('name__trim__length')]:
  169. with self.subTest(expr=expr):
  170. self.assertCountEqual(
  171. Company.objects.annotate(length=expr).values('name', 'length'),
  172. [
  173. {'name': ' Django Software Foundation ', 'length': 26},
  174. {'name': 'Yahoo', 'length': 5},
  175. ],
  176. )
  177. def test_mixed_type_annotation_date_interval(self):
  178. active = datetime.datetime(2015, 3, 20, 14, 0, 0)
  179. duration = datetime.timedelta(hours=1)
  180. expires = datetime.datetime(2015, 3, 20, 14, 0, 0) + duration
  181. Ticket.objects.create(active_at=active, duration=duration)
  182. t = Ticket.objects.annotate(
  183. expires=ExpressionWrapper(F('active_at') + F('duration'), output_field=DateTimeField())
  184. ).first()
  185. self.assertEqual(t.expires, expires)
  186. def test_mixed_type_annotation_numbers(self):
  187. test = self.b1
  188. b = Book.objects.annotate(
  189. combined=ExpressionWrapper(F('pages') + F('rating'), output_field=IntegerField())
  190. ).get(isbn=test.isbn)
  191. combined = int(test.pages + test.rating)
  192. self.assertEqual(b.combined, combined)
  193. def test_empty_expression_annotation(self):
  194. books = Book.objects.annotate(
  195. selected=ExpressionWrapper(Q(pk__in=[]), output_field=BooleanField())
  196. )
  197. self.assertEqual(len(books), Book.objects.count())
  198. self.assertTrue(all(not book.selected for book in books))
  199. books = Book.objects.annotate(
  200. selected=ExpressionWrapper(Q(pk__in=Book.objects.none()), output_field=BooleanField())
  201. )
  202. self.assertEqual(len(books), Book.objects.count())
  203. self.assertTrue(all(not book.selected for book in books))
  204. def test_annotate_with_aggregation(self):
  205. books = Book.objects.annotate(is_book=Value(1), rating_count=Count('rating'))
  206. for book in books:
  207. self.assertEqual(book.is_book, 1)
  208. self.assertEqual(book.rating_count, 1)
  209. def test_combined_expression_annotation_with_aggregation(self):
  210. book = Book.objects.annotate(
  211. combined=ExpressionWrapper(Value(3) * Value(4), output_field=IntegerField()),
  212. rating_count=Count('rating'),
  213. ).first()
  214. self.assertEqual(book.combined, 12)
  215. self.assertEqual(book.rating_count, 1)
  216. def test_combined_f_expression_annotation_with_aggregation(self):
  217. book = Book.objects.filter(isbn='159059725').annotate(
  218. combined=ExpressionWrapper(F('price') * F('pages'), output_field=FloatField()),
  219. rating_count=Count('rating'),
  220. ).first()
  221. self.assertEqual(book.combined, 13410.0)
  222. self.assertEqual(book.rating_count, 1)
  223. @skipUnlessDBFeature('supports_boolean_expr_in_select_clause')
  224. def test_q_expression_annotation_with_aggregation(self):
  225. book = Book.objects.filter(isbn='159059725').annotate(
  226. isnull_pubdate=ExpressionWrapper(
  227. Q(pubdate__isnull=True),
  228. output_field=BooleanField(),
  229. ),
  230. rating_count=Count('rating'),
  231. ).first()
  232. self.assertEqual(book.isnull_pubdate, False)
  233. self.assertEqual(book.rating_count, 1)
  234. @skipUnlessDBFeature('supports_boolean_expr_in_select_clause')
  235. def test_grouping_by_q_expression_annotation(self):
  236. authors = Author.objects.annotate(
  237. under_40=ExpressionWrapper(Q(age__lt=40), output_field=BooleanField()),
  238. ).values('under_40').annotate(
  239. count_id=Count('id'),
  240. ).values('under_40', 'count_id')
  241. self.assertCountEqual(authors, [
  242. {'under_40': False, 'count_id': 3},
  243. {'under_40': True, 'count_id': 6},
  244. ])
  245. def test_aggregate_over_annotation(self):
  246. agg = Author.objects.annotate(other_age=F('age')).aggregate(otherage_sum=Sum('other_age'))
  247. other_agg = Author.objects.aggregate(age_sum=Sum('age'))
  248. self.assertEqual(agg['otherage_sum'], other_agg['age_sum'])
  249. @skipUnlessDBFeature('can_distinct_on_fields')
  250. def test_distinct_on_with_annotation(self):
  251. store = Store.objects.create(
  252. name='test store',
  253. original_opening=datetime.datetime.now(),
  254. friday_night_closing=datetime.time(21, 00, 00),
  255. )
  256. names = [
  257. 'Theodore Roosevelt',
  258. 'Eleanor Roosevelt',
  259. 'Franklin Roosevelt',
  260. 'Ned Stark',
  261. 'Catelyn Stark',
  262. ]
  263. for name in names:
  264. Employee.objects.create(
  265. store=store,
  266. first_name=name.split()[0],
  267. last_name=name.split()[1],
  268. age=30, salary=2000,
  269. )
  270. people = Employee.objects.annotate(
  271. name_lower=Lower('last_name'),
  272. ).distinct('name_lower')
  273. self.assertEqual({p.last_name for p in people}, {'Stark', 'Roosevelt'})
  274. self.assertEqual(len(people), 2)
  275. people2 = Employee.objects.annotate(
  276. test_alias=F('store__name'),
  277. ).distinct('test_alias')
  278. self.assertEqual(len(people2), 1)
  279. lengths = Employee.objects.annotate(
  280. name_len=Length('first_name'),
  281. ).distinct('name_len').values_list('name_len', flat=True)
  282. self.assertCountEqual(lengths, [3, 7, 8])
  283. def test_filter_annotation(self):
  284. books = Book.objects.annotate(is_book=Value(1)).filter(is_book=1)
  285. for book in books:
  286. self.assertEqual(book.is_book, 1)
  287. def test_filter_annotation_with_f(self):
  288. books = Book.objects.annotate(
  289. other_rating=F('rating')
  290. ).filter(other_rating=3.5)
  291. for book in books:
  292. self.assertEqual(book.other_rating, 3.5)
  293. def test_filter_annotation_with_double_f(self):
  294. books = Book.objects.annotate(
  295. other_rating=F('rating')
  296. ).filter(other_rating=F('rating'))
  297. for book in books:
  298. self.assertEqual(book.other_rating, book.rating)
  299. def test_filter_agg_with_double_f(self):
  300. books = Book.objects.annotate(
  301. sum_rating=Sum('rating')
  302. ).filter(sum_rating=F('sum_rating'))
  303. for book in books:
  304. self.assertEqual(book.sum_rating, book.rating)
  305. def test_filter_wrong_annotation(self):
  306. with self.assertRaisesMessage(FieldError, "Cannot resolve keyword 'nope' into field."):
  307. list(Book.objects.annotate(
  308. sum_rating=Sum('rating')
  309. ).filter(sum_rating=F('nope')))
  310. def test_decimal_annotation(self):
  311. salary = Decimal(10) ** -Employee._meta.get_field('salary').decimal_places
  312. Employee.objects.create(
  313. first_name='Max',
  314. last_name='Paine',
  315. store=Store.objects.first(),
  316. age=23,
  317. salary=salary,
  318. )
  319. self.assertEqual(
  320. Employee.objects.annotate(new_salary=F('salary') / 10).get().new_salary,
  321. salary / 10,
  322. )
  323. def test_filter_decimal_annotation(self):
  324. qs = Book.objects.annotate(new_price=F('price') + 1).filter(new_price=Decimal(31)).values_list('new_price')
  325. self.assertEqual(qs.get(), (Decimal(31),))
  326. def test_combined_annotation_commutative(self):
  327. book1 = Book.objects.annotate(adjusted_rating=F('rating') + 2).get(pk=self.b1.pk)
  328. book2 = Book.objects.annotate(adjusted_rating=2 + F('rating')).get(pk=self.b1.pk)
  329. self.assertEqual(book1.adjusted_rating, book2.adjusted_rating)
  330. book1 = Book.objects.annotate(adjusted_rating=F('rating') + None).get(pk=self.b1.pk)
  331. book2 = Book.objects.annotate(adjusted_rating=None + F('rating')).get(pk=self.b1.pk)
  332. self.assertEqual(book1.adjusted_rating, book2.adjusted_rating)
  333. def test_update_with_annotation(self):
  334. book_preupdate = Book.objects.get(pk=self.b2.pk)
  335. Book.objects.annotate(other_rating=F('rating') - 1).update(rating=F('other_rating'))
  336. book_postupdate = Book.objects.get(pk=self.b2.pk)
  337. self.assertEqual(book_preupdate.rating - 1, book_postupdate.rating)
  338. def test_annotation_with_m2m(self):
  339. books = Book.objects.annotate(author_age=F('authors__age')).filter(pk=self.b1.pk).order_by('author_age')
  340. self.assertEqual(books[0].author_age, 34)
  341. self.assertEqual(books[1].author_age, 35)
  342. def test_annotation_reverse_m2m(self):
  343. books = Book.objects.annotate(
  344. store_name=F('store__name'),
  345. ).filter(
  346. name='Practical Django Projects',
  347. ).order_by('store_name')
  348. self.assertQuerysetEqual(
  349. books, [
  350. 'Amazon.com',
  351. 'Books.com',
  352. 'Mamma and Pappa\'s Books'
  353. ],
  354. lambda b: b.store_name
  355. )
  356. def test_values_annotation(self):
  357. """
  358. Annotations can reference fields in a values clause,
  359. and contribute to an existing values clause.
  360. """
  361. # annotate references a field in values()
  362. qs = Book.objects.values('rating').annotate(other_rating=F('rating') - 1)
  363. book = qs.get(pk=self.b1.pk)
  364. self.assertEqual(book['rating'] - 1, book['other_rating'])
  365. # filter refs the annotated value
  366. book = qs.get(other_rating=4)
  367. self.assertEqual(book['other_rating'], 4)
  368. # can annotate an existing values with a new field
  369. book = qs.annotate(other_isbn=F('isbn')).get(other_rating=4)
  370. self.assertEqual(book['other_rating'], 4)
  371. self.assertEqual(book['other_isbn'], '155860191')
  372. def test_values_with_pk_annotation(self):
  373. # annotate references a field in values() with pk
  374. publishers = Publisher.objects.values('id', 'book__rating').annotate(total=Sum('book__rating'))
  375. for publisher in publishers.filter(pk=self.p1.pk):
  376. self.assertEqual(publisher['book__rating'], publisher['total'])
  377. @skipUnlessDBFeature('allows_group_by_pk')
  378. def test_rawsql_group_by_collapse(self):
  379. raw = RawSQL('SELECT MIN(id) FROM annotations_book', [])
  380. qs = Author.objects.values('id').annotate(
  381. min_book_id=raw,
  382. count_friends=Count('friends'),
  383. ).order_by()
  384. _, _, group_by = qs.query.get_compiler(using='default').pre_sql_setup()
  385. self.assertEqual(len(group_by), 1)
  386. self.assertNotEqual(raw, group_by[0])
  387. def test_defer_annotation(self):
  388. """
  389. Deferred attributes can be referenced by an annotation,
  390. but they are not themselves deferred, and cannot be deferred.
  391. """
  392. qs = Book.objects.defer('rating').annotate(other_rating=F('rating') - 1)
  393. with self.assertNumQueries(2):
  394. book = qs.get(other_rating=4)
  395. self.assertEqual(book.rating, 5)
  396. self.assertEqual(book.other_rating, 4)
  397. with self.assertRaisesMessage(FieldDoesNotExist, "Book has no field named 'other_rating'"):
  398. book = qs.defer('other_rating').get(other_rating=4)
  399. def test_mti_annotations(self):
  400. """
  401. Fields on an inherited model can be referenced by an
  402. annotated field.
  403. """
  404. d = DepartmentStore.objects.create(
  405. name='Angus & Robinson',
  406. original_opening=datetime.date(2014, 3, 8),
  407. friday_night_closing=datetime.time(21, 00, 00),
  408. chain='Westfield'
  409. )
  410. books = Book.objects.filter(rating__gt=4)
  411. for b in books:
  412. d.books.add(b)
  413. qs = DepartmentStore.objects.annotate(
  414. other_name=F('name'),
  415. other_chain=F('chain'),
  416. is_open=Value(True, BooleanField()),
  417. book_isbn=F('books__isbn')
  418. ).order_by('book_isbn').filter(chain='Westfield')
  419. self.assertQuerysetEqual(
  420. qs, [
  421. ('Angus & Robinson', 'Westfield', True, '155860191'),
  422. ('Angus & Robinson', 'Westfield', True, '159059725')
  423. ],
  424. lambda d: (d.other_name, d.other_chain, d.is_open, d.book_isbn)
  425. )
  426. def test_null_annotation(self):
  427. """
  428. Annotating None onto a model round-trips
  429. """
  430. book = Book.objects.annotate(no_value=Value(None, output_field=IntegerField())).first()
  431. self.assertIsNone(book.no_value)
  432. def test_order_by_annotation(self):
  433. authors = Author.objects.annotate(other_age=F('age')).order_by('other_age')
  434. self.assertQuerysetEqual(
  435. authors, [
  436. 25, 29, 29, 34, 35, 37, 45, 46, 57,
  437. ],
  438. lambda a: a.other_age
  439. )
  440. def test_order_by_aggregate(self):
  441. authors = Author.objects.values('age').annotate(age_count=Count('age')).order_by('age_count', 'age')
  442. self.assertQuerysetEqual(
  443. authors, [
  444. (25, 1), (34, 1), (35, 1), (37, 1), (45, 1), (46, 1), (57, 1), (29, 2),
  445. ],
  446. lambda a: (a['age'], a['age_count'])
  447. )
  448. def test_raw_sql_with_inherited_field(self):
  449. DepartmentStore.objects.create(
  450. name='Angus & Robinson',
  451. original_opening=datetime.date(2014, 3, 8),
  452. friday_night_closing=datetime.time(21),
  453. chain='Westfield',
  454. area=123,
  455. )
  456. tests = (
  457. ('name', 'Angus & Robinson'),
  458. ('surface', 123),
  459. ("case when name='Angus & Robinson' then chain else name end", 'Westfield'),
  460. )
  461. for sql, expected_result in tests:
  462. with self.subTest(sql=sql):
  463. self.assertSequenceEqual(
  464. DepartmentStore.objects.annotate(
  465. annotation=RawSQL(sql, ()),
  466. ).values_list('annotation', flat=True),
  467. [expected_result],
  468. )
  469. def test_annotate_exists(self):
  470. authors = Author.objects.annotate(c=Count('id')).filter(c__gt=1)
  471. self.assertFalse(authors.exists())
  472. def test_column_field_ordering(self):
  473. """
  474. Columns are aligned in the correct order for resolve_columns. This test
  475. will fail on MySQL if column ordering is out. Column fields should be
  476. aligned as:
  477. 1. extra_select
  478. 2. model_fields
  479. 3. annotation_fields
  480. 4. model_related_fields
  481. """
  482. store = Store.objects.first()
  483. Employee.objects.create(id=1, first_name='Max', manager=True, last_name='Paine',
  484. store=store, age=23, salary=Decimal(50000.00))
  485. Employee.objects.create(id=2, first_name='Buffy', manager=False, last_name='Summers',
  486. store=store, age=18, salary=Decimal(40000.00))
  487. qs = Employee.objects.extra(
  488. select={'random_value': '42'}
  489. ).select_related('store').annotate(
  490. annotated_value=Value(17),
  491. )
  492. rows = [
  493. (1, 'Max', True, 42, 'Paine', 23, Decimal(50000.00), store.name, 17),
  494. (2, 'Buffy', False, 42, 'Summers', 18, Decimal(40000.00), store.name, 17)
  495. ]
  496. self.assertQuerysetEqual(
  497. qs.order_by('id'), rows,
  498. lambda e: (
  499. e.id, e.first_name, e.manager, e.random_value, e.last_name, e.age,
  500. e.salary, e.store.name, e.annotated_value))
  501. def test_column_field_ordering_with_deferred(self):
  502. store = Store.objects.first()
  503. Employee.objects.create(id=1, first_name='Max', manager=True, last_name='Paine',
  504. store=store, age=23, salary=Decimal(50000.00))
  505. Employee.objects.create(id=2, first_name='Buffy', manager=False, last_name='Summers',
  506. store=store, age=18, salary=Decimal(40000.00))
  507. qs = Employee.objects.extra(
  508. select={'random_value': '42'}
  509. ).select_related('store').annotate(
  510. annotated_value=Value(17),
  511. )
  512. rows = [
  513. (1, 'Max', True, 42, 'Paine', 23, Decimal(50000.00), store.name, 17),
  514. (2, 'Buffy', False, 42, 'Summers', 18, Decimal(40000.00), store.name, 17)
  515. ]
  516. # and we respect deferred columns!
  517. self.assertQuerysetEqual(
  518. qs.defer('age').order_by('id'), rows,
  519. lambda e: (
  520. e.id, e.first_name, e.manager, e.random_value, e.last_name, e.age,
  521. e.salary, e.store.name, e.annotated_value))
  522. @cxOracle_py3_bug
  523. def test_custom_functions(self):
  524. Company(name='Apple', motto=None, ticker_name='APPL', description='Beautiful Devices').save()
  525. Company(name='Django Software Foundation', motto=None, ticker_name=None, description=None).save()
  526. Company(name='Google', motto='Do No Evil', ticker_name='GOOG', description='Internet Company').save()
  527. Company(name='Yahoo', motto=None, ticker_name=None, description='Internet Company').save()
  528. qs = Company.objects.annotate(
  529. tagline=Func(
  530. F('motto'),
  531. F('ticker_name'),
  532. F('description'),
  533. Value('No Tag'),
  534. function='COALESCE'
  535. )
  536. ).order_by('name')
  537. self.assertQuerysetEqual(
  538. qs, [
  539. ('Apple', 'APPL'),
  540. ('Django Software Foundation', 'No Tag'),
  541. ('Google', 'Do No Evil'),
  542. ('Yahoo', 'Internet Company')
  543. ],
  544. lambda c: (c.name, c.tagline)
  545. )
  546. @cxOracle_py3_bug
  547. def test_custom_functions_can_ref_other_functions(self):
  548. Company(name='Apple', motto=None, ticker_name='APPL', description='Beautiful Devices').save()
  549. Company(name='Django Software Foundation', motto=None, ticker_name=None, description=None).save()
  550. Company(name='Google', motto='Do No Evil', ticker_name='GOOG', description='Internet Company').save()
  551. Company(name='Yahoo', motto=None, ticker_name=None, description='Internet Company').save()
  552. class Lower(Func):
  553. function = 'LOWER'
  554. qs = Company.objects.annotate(
  555. tagline=Func(
  556. F('motto'),
  557. F('ticker_name'),
  558. F('description'),
  559. Value('No Tag'),
  560. function='COALESCE',
  561. )
  562. ).annotate(
  563. tagline_lower=Lower(F('tagline')),
  564. ).order_by('name')
  565. # LOWER function supported by:
  566. # oracle, postgres, mysql, sqlite, sqlserver
  567. self.assertQuerysetEqual(
  568. qs, [
  569. ('Apple', 'APPL'.lower()),
  570. ('Django Software Foundation', 'No Tag'.lower()),
  571. ('Google', 'Do No Evil'.lower()),
  572. ('Yahoo', 'Internet Company'.lower())
  573. ],
  574. lambda c: (c.name, c.tagline_lower)
  575. )
  576. def test_boolean_value_annotation(self):
  577. books = Book.objects.annotate(
  578. is_book=Value(True, output_field=BooleanField()),
  579. is_pony=Value(False, output_field=BooleanField()),
  580. is_none=Value(None, output_field=BooleanField(null=True)),
  581. is_none_old=Value(None, output_field=NullBooleanField()),
  582. )
  583. self.assertGreater(len(books), 0)
  584. for book in books:
  585. self.assertIs(book.is_book, True)
  586. self.assertIs(book.is_pony, False)
  587. self.assertIsNone(book.is_none)
  588. self.assertIsNone(book.is_none_old)
  589. def test_annotation_in_f_grouped_by_annotation(self):
  590. qs = (
  591. Publisher.objects.annotate(multiplier=Value(3))
  592. # group by option => sum of value * multiplier
  593. .values('name')
  594. .annotate(multiplied_value_sum=Sum(F('multiplier') * F('num_awards')))
  595. .order_by()
  596. )
  597. self.assertCountEqual(
  598. qs, [
  599. {'multiplied_value_sum': 9, 'name': 'Apress'},
  600. {'multiplied_value_sum': 0, 'name': "Jonno's House of Books"},
  601. {'multiplied_value_sum': 27, 'name': 'Morgan Kaufmann'},
  602. {'multiplied_value_sum': 21, 'name': 'Prentice Hall'},
  603. {'multiplied_value_sum': 3, 'name': 'Sams'},
  604. ]
  605. )
  606. def test_arguments_must_be_expressions(self):
  607. msg = 'QuerySet.annotate() received non-expression(s): %s.'
  608. with self.assertRaisesMessage(TypeError, msg % BooleanField()):
  609. Book.objects.annotate(BooleanField())
  610. with self.assertRaisesMessage(TypeError, msg % True):
  611. Book.objects.annotate(is_book=True)
  612. with self.assertRaisesMessage(TypeError, msg % ', '.join([str(BooleanField()), 'True'])):
  613. Book.objects.annotate(BooleanField(), Value(False), is_book=True)
  614. def test_chaining_annotation_filter_with_m2m(self):
  615. qs = Author.objects.filter(
  616. name='Adrian Holovaty',
  617. friends__age=35,
  618. ).annotate(
  619. jacob_name=F('friends__name'),
  620. ).filter(
  621. friends__age=29,
  622. ).annotate(
  623. james_name=F('friends__name'),
  624. ).values('jacob_name', 'james_name')
  625. self.assertCountEqual(
  626. qs,
  627. [{'jacob_name': 'Jacob Kaplan-Moss', 'james_name': 'James Bennett'}],
  628. )
  629. def test_annotation_filter_with_subquery(self):
  630. long_books_qs = Book.objects.filter(
  631. publisher=OuterRef('pk'),
  632. pages__gt=400,
  633. ).values('publisher').annotate(count=Count('pk')).values('count')
  634. publisher_books_qs = Publisher.objects.annotate(
  635. total_books=Count('book'),
  636. ).filter(
  637. total_books=Subquery(long_books_qs, output_field=IntegerField()),
  638. ).values('name')
  639. self.assertCountEqual(publisher_books_qs, [{'name': 'Sams'}, {'name': 'Morgan Kaufmann'}])
  640. def test_annotation_exists_aggregate_values_chaining(self):
  641. qs = Book.objects.values('publisher').annotate(
  642. has_authors=Exists(Book.authors.through.objects.filter(book=OuterRef('pk'))),
  643. max_pubdate=Max('pubdate'),
  644. ).values_list('max_pubdate', flat=True).order_by('max_pubdate')
  645. self.assertCountEqual(qs, [
  646. datetime.date(1991, 10, 15),
  647. datetime.date(2008, 3, 3),
  648. datetime.date(2008, 6, 23),
  649. datetime.date(2008, 11, 3),
  650. ])
  651. @skipUnlessDBFeature('supports_subqueries_in_group_by')
  652. def test_annotation_subquery_and_aggregate_values_chaining(self):
  653. qs = Book.objects.annotate(
  654. pub_year=ExtractYear('pubdate')
  655. ).values('pub_year').annotate(
  656. top_rating=Subquery(
  657. Book.objects.filter(
  658. pubdate__year=OuterRef('pub_year')
  659. ).order_by('-rating').values('rating')[:1]
  660. ),
  661. total_pages=Sum('pages'),
  662. ).values('pub_year', 'total_pages', 'top_rating')
  663. self.assertCountEqual(qs, [
  664. {'pub_year': 1991, 'top_rating': 5.0, 'total_pages': 946},
  665. {'pub_year': 1995, 'top_rating': 4.0, 'total_pages': 1132},
  666. {'pub_year': 2007, 'top_rating': 4.5, 'total_pages': 447},
  667. {'pub_year': 2008, 'top_rating': 4.0, 'total_pages': 1178},
  668. ])
  669. def test_annotation_subquery_outerref_transform(self):
  670. qs = Book.objects.annotate(
  671. top_rating_year=Subquery(
  672. Book.objects.filter(
  673. pubdate__year=OuterRef('pubdate__year')
  674. ).order_by('-rating').values('rating')[:1]
  675. ),
  676. ).values('pubdate__year', 'top_rating_year')
  677. self.assertCountEqual(qs, [
  678. {'pubdate__year': 1991, 'top_rating_year': 5.0},
  679. {'pubdate__year': 1995, 'top_rating_year': 4.0},
  680. {'pubdate__year': 2007, 'top_rating_year': 4.5},
  681. {'pubdate__year': 2008, 'top_rating_year': 4.0},
  682. {'pubdate__year': 2008, 'top_rating_year': 4.0},
  683. {'pubdate__year': 2008, 'top_rating_year': 4.0},
  684. ])
  685. def test_annotation_aggregate_with_m2o(self):
  686. if connection.vendor == 'mysql' and 'ONLY_FULL_GROUP_BY' in connection.sql_mode:
  687. self.skipTest(
  688. 'GROUP BY optimization does not work properly when '
  689. 'ONLY_FULL_GROUP_BY mode is enabled on MySQL, see #31331.'
  690. )
  691. qs = Author.objects.filter(age__lt=30).annotate(
  692. max_pages=Case(
  693. When(book_contact_set__isnull=True, then=Value(0)),
  694. default=Max(F('book__pages')),
  695. ),
  696. ).values('name', 'max_pages')
  697. self.assertCountEqual(qs, [
  698. {'name': 'James Bennett', 'max_pages': 300},
  699. {'name': 'Paul Bissex', 'max_pages': 0},
  700. {'name': 'Wesley J. Chun', 'max_pages': 0},
  701. ])
  702. class AliasTests(TestCase):
  703. @classmethod
  704. def setUpTestData(cls):
  705. cls.a1 = Author.objects.create(name='Adrian Holovaty', age=34)
  706. cls.a2 = Author.objects.create(name='Jacob Kaplan-Moss', age=35)
  707. cls.a3 = Author.objects.create(name='James Bennett', age=34)
  708. cls.a4 = Author.objects.create(name='Peter Norvig', age=57)
  709. cls.a5 = Author.objects.create(name='Stuart Russell', age=46)
  710. p1 = Publisher.objects.create(name='Apress', num_awards=3)
  711. cls.b1 = Book.objects.create(
  712. isbn='159059725', pages=447, rating=4.5, price=Decimal('30.00'),
  713. contact=cls.a1, publisher=p1, pubdate=datetime.date(2007, 12, 6),
  714. name='The Definitive Guide to Django: Web Development Done Right',
  715. )
  716. cls.b2 = Book.objects.create(
  717. isbn='159059996', pages=300, rating=4.0, price=Decimal('29.69'),
  718. contact=cls.a3, publisher=p1, pubdate=datetime.date(2008, 6, 23),
  719. name='Practical Django Projects',
  720. )
  721. cls.b3 = Book.objects.create(
  722. isbn='013790395', pages=1132, rating=4.0, price=Decimal('82.80'),
  723. contact=cls.a4, publisher=p1, pubdate=datetime.date(1995, 1, 15),
  724. name='Artificial Intelligence: A Modern Approach',
  725. )
  726. cls.b4 = Book.objects.create(
  727. isbn='155860191', pages=946, rating=5.0, price=Decimal('75.00'),
  728. contact=cls.a4, publisher=p1, pubdate=datetime.date(1991, 10, 15),
  729. name='Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp',
  730. )
  731. cls.b1.authors.add(cls.a1, cls.a2)
  732. cls.b2.authors.add(cls.a3)
  733. cls.b3.authors.add(cls.a4, cls.a5)
  734. cls.b4.authors.add(cls.a4)
  735. Store.objects.create(
  736. name='Amazon.com',
  737. original_opening=datetime.datetime(1994, 4, 23, 9, 17, 42),
  738. friday_night_closing=datetime.time(23, 59, 59)
  739. )
  740. Store.objects.create(
  741. name='Books.com',
  742. original_opening=datetime.datetime(2001, 3, 15, 11, 23, 37),
  743. friday_night_closing=datetime.time(23, 59, 59)
  744. )
  745. def test_basic_alias(self):
  746. qs = Book.objects.alias(is_book=Value(1))
  747. self.assertIs(hasattr(qs.first(), 'is_book'), False)
  748. def test_basic_alias_annotation(self):
  749. qs = Book.objects.alias(
  750. is_book_alias=Value(1),
  751. ).annotate(is_book=F('is_book_alias'))
  752. self.assertIs(hasattr(qs.first(), 'is_book_alias'), False)
  753. for book in qs:
  754. with self.subTest(book=book):
  755. self.assertEqual(book.is_book, 1)
  756. def test_basic_alias_f_annotation(self):
  757. qs = Book.objects.alias(
  758. another_rating_alias=F('rating')
  759. ).annotate(another_rating=F('another_rating_alias'))
  760. self.assertIs(hasattr(qs.first(), 'another_rating_alias'), False)
  761. for book in qs:
  762. with self.subTest(book=book):
  763. self.assertEqual(book.another_rating, book.rating)
  764. def test_basic_alias_f_transform_annotation(self):
  765. qs = Book.objects.alias(
  766. pubdate_alias=F('pubdate'),
  767. ).annotate(pubdate_year=F('pubdate_alias__year'))
  768. self.assertIs(hasattr(qs.first(), 'pubdate_alias'), False)
  769. for book in qs:
  770. with self.subTest(book=book):
  771. self.assertEqual(book.pubdate_year, book.pubdate.year)
  772. def test_alias_after_annotation(self):
  773. qs = Book.objects.annotate(
  774. is_book=Value(1),
  775. ).alias(is_book_alias=F('is_book'))
  776. book = qs.first()
  777. self.assertIs(hasattr(book, 'is_book'), True)
  778. self.assertIs(hasattr(book, 'is_book_alias'), False)
  779. def test_overwrite_annotation_with_alias(self):
  780. qs = Book.objects.annotate(is_book=Value(1)).alias(is_book=F('is_book'))
  781. self.assertIs(hasattr(qs.first(), 'is_book'), False)
  782. def test_overwrite_alias_with_annotation(self):
  783. qs = Book.objects.alias(is_book=Value(1)).annotate(is_book=F('is_book'))
  784. for book in qs:
  785. with self.subTest(book=book):
  786. self.assertEqual(book.is_book, 1)
  787. def test_alias_annotation_expression(self):
  788. qs = Book.objects.alias(
  789. is_book_alias=Value(1),
  790. ).annotate(is_book=Coalesce('is_book_alias', 0))
  791. self.assertIs(hasattr(qs.first(), 'is_book_alias'), False)
  792. for book in qs:
  793. with self.subTest(book=book):
  794. self.assertEqual(book.is_book, 1)
  795. def test_alias_default_alias_expression(self):
  796. qs = Author.objects.alias(
  797. Sum('book__pages'),
  798. ).filter(book__pages__sum__gt=2000)
  799. self.assertIs(hasattr(qs.first(), 'book__pages__sum'), False)
  800. self.assertSequenceEqual(qs, [self.a4])
  801. def test_joined_alias_annotation(self):
  802. qs = Book.objects.select_related('publisher').alias(
  803. num_awards_alias=F('publisher__num_awards'),
  804. ).annotate(num_awards=F('num_awards_alias'))
  805. self.assertIs(hasattr(qs.first(), 'num_awards_alias'), False)
  806. for book in qs:
  807. with self.subTest(book=book):
  808. self.assertEqual(book.num_awards, book.publisher.num_awards)
  809. def test_alias_annotate_with_aggregation(self):
  810. qs = Book.objects.alias(
  811. is_book_alias=Value(1),
  812. rating_count_alias=Count('rating'),
  813. ).annotate(
  814. is_book=F('is_book_alias'),
  815. rating_count=F('rating_count_alias'),
  816. )
  817. book = qs.first()
  818. self.assertIs(hasattr(book, 'is_book_alias'), False)
  819. self.assertIs(hasattr(book, 'rating_count_alias'), False)
  820. for book in qs:
  821. with self.subTest(book=book):
  822. self.assertEqual(book.is_book, 1)
  823. self.assertEqual(book.rating_count, 1)
  824. def test_filter_alias_with_f(self):
  825. qs = Book.objects.alias(
  826. other_rating=F('rating'),
  827. ).filter(other_rating=4.5)
  828. self.assertIs(hasattr(qs.first(), 'other_rating'), False)
  829. self.assertSequenceEqual(qs, [self.b1])
  830. def test_filter_alias_with_double_f(self):
  831. qs = Book.objects.alias(
  832. other_rating=F('rating'),
  833. ).filter(other_rating=F('rating'))
  834. self.assertIs(hasattr(qs.first(), 'other_rating'), False)
  835. self.assertEqual(qs.count(), Book.objects.count())
  836. def test_filter_alias_agg_with_double_f(self):
  837. qs = Book.objects.alias(
  838. sum_rating=Sum('rating'),
  839. ).filter(sum_rating=F('sum_rating'))
  840. self.assertIs(hasattr(qs.first(), 'sum_rating'), False)
  841. self.assertEqual(qs.count(), Book.objects.count())
  842. def test_update_with_alias(self):
  843. Book.objects.alias(
  844. other_rating=F('rating') - 1,
  845. ).update(rating=F('other_rating'))
  846. self.b1.refresh_from_db()
  847. self.assertEqual(self.b1.rating, 3.5)
  848. def test_order_by_alias(self):
  849. qs = Author.objects.alias(other_age=F('age')).order_by('other_age')
  850. self.assertIs(hasattr(qs.first(), 'other_age'), False)
  851. self.assertQuerysetEqual(qs, [34, 34, 35, 46, 57], lambda a: a.age)
  852. def test_order_by_alias_aggregate(self):
  853. qs = Author.objects.values('age').alias(age_count=Count('age')).order_by('age_count', 'age')
  854. self.assertIs(hasattr(qs.first(), 'age_count'), False)
  855. self.assertQuerysetEqual(qs, [35, 46, 57, 34], lambda a: a['age'])
  856. def test_dates_alias(self):
  857. qs = Book.objects.alias(
  858. pubdate_alias=F('pubdate'),
  859. ).dates('pubdate_alias', 'month')
  860. self.assertCountEqual(qs, [
  861. datetime.date(1991, 10, 1),
  862. datetime.date(1995, 1, 1),
  863. datetime.date(2007, 12, 1),
  864. datetime.date(2008, 6, 1),
  865. ])
  866. def test_datetimes_alias(self):
  867. qs = Store.objects.alias(
  868. original_opening_alias=F('original_opening'),
  869. ).datetimes('original_opening_alias', 'year')
  870. self.assertCountEqual(qs, [
  871. datetime.datetime(1994, 1, 1),
  872. datetime.datetime(2001, 1, 1),
  873. ])
  874. def test_aggregate_alias(self):
  875. msg = (
  876. "Cannot aggregate over the 'other_age' alias. Use annotate() to "
  877. "promote it."
  878. )
  879. with self.assertRaisesMessage(FieldError, msg):
  880. Author.objects.alias(
  881. other_age=F('age'),
  882. ).aggregate(otherage_sum=Sum('other_age'))
  883. def test_defer_only_alias(self):
  884. qs = Book.objects.alias(rating_alias=F('rating') - 1)
  885. msg = "Book has no field named 'rating_alias'"
  886. for operation in ['defer', 'only']:
  887. with self.subTest(operation=operation):
  888. with self.assertRaisesMessage(FieldDoesNotExist, msg):
  889. getattr(qs, operation)('rating_alias').first()
  890. @skipUnlessDBFeature('can_distinct_on_fields')
  891. def test_distinct_on_alias(self):
  892. qs = Book.objects.alias(rating_alias=F('rating') - 1)
  893. msg = "Cannot resolve keyword 'rating_alias' into field."
  894. with self.assertRaisesMessage(FieldError, msg):
  895. qs.distinct('rating_alias').first()
  896. def test_values_alias(self):
  897. qs = Book.objects.alias(rating_alias=F('rating') - 1)
  898. msg = (
  899. "Cannot select the 'rating_alias' alias. Use annotate() to "
  900. "promote it."
  901. )
  902. for operation in ['values', 'values_list']:
  903. with self.subTest(operation=operation):
  904. with self.assertRaisesMessage(FieldError, msg):
  905. getattr(qs, operation)('rating_alias')