tests.py 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462
  1. import datetime
  2. from decimal import Decimal
  3. from unittest import skipUnless
  4. from django.core.exceptions import FieldDoesNotExist, FieldError
  5. from django.db import connection
  6. from django.db.models import (
  7. BooleanField,
  8. Case,
  9. CharField,
  10. Count,
  11. DateTimeField,
  12. DecimalField,
  13. Exists,
  14. ExpressionWrapper,
  15. F,
  16. FloatField,
  17. Func,
  18. IntegerField,
  19. JSONField,
  20. Max,
  21. OuterRef,
  22. Q,
  23. Subquery,
  24. Sum,
  25. Value,
  26. When,
  27. )
  28. from django.db.models.expressions import RawSQL
  29. from django.db.models.functions import (
  30. Cast,
  31. Coalesce,
  32. ExtractYear,
  33. Floor,
  34. Length,
  35. Lower,
  36. Trim,
  37. )
  38. from django.db.models.sql.query import get_field_names_from_opts
  39. from django.test import TestCase, skipUnlessDBFeature
  40. from django.test.utils import register_lookup
  41. from .models import (
  42. Author,
  43. Book,
  44. Company,
  45. DepartmentStore,
  46. Employee,
  47. JsonModel,
  48. Publisher,
  49. Store,
  50. Ticket,
  51. )
  52. class NonAggregateAnnotationTestCase(TestCase):
  53. @classmethod
  54. def setUpTestData(cls):
  55. cls.a1 = Author.objects.create(name="Adrian Holovaty", age=34)
  56. cls.a2 = Author.objects.create(name="Jacob Kaplan-Moss", age=35)
  57. cls.a3 = Author.objects.create(name="Brad Dayley", age=45)
  58. cls.a4 = Author.objects.create(name="James Bennett", age=29)
  59. cls.a5 = Author.objects.create(name="Jeffrey Forcier", age=37)
  60. cls.a6 = Author.objects.create(name="Paul Bissex", age=29)
  61. cls.a7 = Author.objects.create(name="Wesley J. Chun", age=25)
  62. cls.a8 = Author.objects.create(name="Peter Norvig", age=57)
  63. cls.a9 = Author.objects.create(name="Stuart Russell", age=46)
  64. cls.a1.friends.add(cls.a2, cls.a4)
  65. cls.a2.friends.add(cls.a1, cls.a7)
  66. cls.a4.friends.add(cls.a1)
  67. cls.a5.friends.add(cls.a6, cls.a7)
  68. cls.a6.friends.add(cls.a5, cls.a7)
  69. cls.a7.friends.add(cls.a2, cls.a5, cls.a6)
  70. cls.a8.friends.add(cls.a9)
  71. cls.a9.friends.add(cls.a8)
  72. cls.p1 = Publisher.objects.create(name="Apress", num_awards=3)
  73. cls.p2 = Publisher.objects.create(name="Sams", num_awards=1)
  74. cls.p3 = Publisher.objects.create(name="Prentice Hall", num_awards=7)
  75. cls.p4 = Publisher.objects.create(name="Morgan Kaufmann", num_awards=9)
  76. cls.p5 = Publisher.objects.create(name="Jonno's House of Books", num_awards=0)
  77. cls.b1 = Book.objects.create(
  78. isbn="159059725",
  79. name="The Definitive Guide to Django: Web Development Done Right",
  80. pages=447,
  81. rating=4.5,
  82. price=Decimal("30.00"),
  83. contact=cls.a1,
  84. publisher=cls.p1,
  85. pubdate=datetime.date(2007, 12, 6),
  86. )
  87. cls.b2 = Book.objects.create(
  88. isbn="067232959",
  89. name="Sams Teach Yourself Django in 24 Hours",
  90. pages=528,
  91. rating=3.0,
  92. price=Decimal("23.09"),
  93. contact=cls.a3,
  94. publisher=cls.p2,
  95. pubdate=datetime.date(2008, 3, 3),
  96. )
  97. cls.b3 = Book.objects.create(
  98. isbn="159059996",
  99. name="Practical Django Projects",
  100. pages=300,
  101. rating=4.0,
  102. price=Decimal("29.69"),
  103. contact=cls.a4,
  104. publisher=cls.p1,
  105. pubdate=datetime.date(2008, 6, 23),
  106. )
  107. cls.b4 = Book.objects.create(
  108. isbn="013235613",
  109. name="Python Web Development with Django",
  110. pages=350,
  111. rating=4.0,
  112. price=Decimal("29.69"),
  113. contact=cls.a5,
  114. publisher=cls.p3,
  115. pubdate=datetime.date(2008, 11, 3),
  116. )
  117. cls.b5 = Book.objects.create(
  118. isbn="013790395",
  119. name="Artificial Intelligence: A Modern Approach",
  120. pages=1132,
  121. rating=4.0,
  122. price=Decimal("82.80"),
  123. contact=cls.a8,
  124. publisher=cls.p3,
  125. pubdate=datetime.date(1995, 1, 15),
  126. )
  127. cls.b6 = Book.objects.create(
  128. isbn="155860191",
  129. name=(
  130. "Paradigms of Artificial Intelligence Programming: Case Studies in "
  131. "Common Lisp"
  132. ),
  133. pages=946,
  134. rating=5.0,
  135. price=Decimal("75.00"),
  136. contact=cls.a8,
  137. publisher=cls.p4,
  138. pubdate=datetime.date(1991, 10, 15),
  139. )
  140. cls.b1.authors.add(cls.a1, cls.a2)
  141. cls.b2.authors.add(cls.a3)
  142. cls.b3.authors.add(cls.a4)
  143. cls.b4.authors.add(cls.a5, cls.a6, cls.a7)
  144. cls.b5.authors.add(cls.a8, cls.a9)
  145. cls.b6.authors.add(cls.a8)
  146. cls.s1 = Store.objects.create(
  147. name="Amazon.com",
  148. original_opening=datetime.datetime(1994, 4, 23, 9, 17, 42),
  149. friday_night_closing=datetime.time(23, 59, 59),
  150. )
  151. cls.s2 = Store.objects.create(
  152. name="Books.com",
  153. original_opening=datetime.datetime(2001, 3, 15, 11, 23, 37),
  154. friday_night_closing=datetime.time(23, 59, 59),
  155. )
  156. cls.s3 = Store.objects.create(
  157. name="Mamma and Pappa's Books",
  158. original_opening=datetime.datetime(1945, 4, 25, 16, 24, 14),
  159. friday_night_closing=datetime.time(21, 30),
  160. )
  161. cls.s1.books.add(cls.b1, cls.b2, cls.b3, cls.b4, cls.b5, cls.b6)
  162. cls.s2.books.add(cls.b1, cls.b3, cls.b5, cls.b6)
  163. cls.s3.books.add(cls.b3, cls.b4, cls.b6)
  164. def test_basic_annotation(self):
  165. books = Book.objects.annotate(is_book=Value(1))
  166. for book in books:
  167. self.assertEqual(book.is_book, 1)
  168. def test_basic_f_annotation(self):
  169. books = Book.objects.annotate(another_rating=F("rating"))
  170. for book in books:
  171. self.assertEqual(book.another_rating, book.rating)
  172. def test_joined_annotation(self):
  173. books = Book.objects.select_related("publisher").annotate(
  174. num_awards=F("publisher__num_awards")
  175. )
  176. for book in books:
  177. self.assertEqual(book.num_awards, book.publisher.num_awards)
  178. def test_joined_transformed_annotation(self):
  179. Employee.objects.bulk_create(
  180. [
  181. Employee(
  182. first_name="John",
  183. last_name="Doe",
  184. age=18,
  185. store=self.s1,
  186. salary=15000,
  187. ),
  188. Employee(
  189. first_name="Jane",
  190. last_name="Jones",
  191. age=30,
  192. store=self.s2,
  193. salary=30000,
  194. ),
  195. Employee(
  196. first_name="Jo",
  197. last_name="Smith",
  198. age=55,
  199. store=self.s3,
  200. salary=50000,
  201. ),
  202. ]
  203. )
  204. employees = Employee.objects.annotate(
  205. store_opened_year=F("store__original_opening__year"),
  206. )
  207. for employee in employees:
  208. self.assertEqual(
  209. employee.store_opened_year,
  210. employee.store.original_opening.year,
  211. )
  212. def test_custom_transform_annotation(self):
  213. with register_lookup(DecimalField, Floor):
  214. books = Book.objects.annotate(floor_price=F("price__floor"))
  215. self.assertCountEqual(
  216. books.values_list("pk", "floor_price"),
  217. [
  218. (self.b1.pk, 30),
  219. (self.b2.pk, 23),
  220. (self.b3.pk, 29),
  221. (self.b4.pk, 29),
  222. (self.b5.pk, 82),
  223. (self.b6.pk, 75),
  224. ],
  225. )
  226. def test_chaining_transforms(self):
  227. Company.objects.create(name=" Django Software Foundation ")
  228. Company.objects.create(name="Yahoo")
  229. with register_lookup(CharField, Trim), register_lookup(CharField, Length):
  230. for expr in [Length("name__trim"), F("name__trim__length")]:
  231. with self.subTest(expr=expr):
  232. self.assertCountEqual(
  233. Company.objects.annotate(length=expr).values("name", "length"),
  234. [
  235. {"name": " Django Software Foundation ", "length": 26},
  236. {"name": "Yahoo", "length": 5},
  237. ],
  238. )
  239. def test_mixed_type_annotation_date_interval(self):
  240. active = datetime.datetime(2015, 3, 20, 14, 0, 0)
  241. duration = datetime.timedelta(hours=1)
  242. expires = datetime.datetime(2015, 3, 20, 14, 0, 0) + duration
  243. Ticket.objects.create(active_at=active, duration=duration)
  244. t = Ticket.objects.annotate(
  245. expires=ExpressionWrapper(
  246. F("active_at") + F("duration"), output_field=DateTimeField()
  247. )
  248. ).first()
  249. self.assertEqual(t.expires, expires)
  250. def test_mixed_type_annotation_numbers(self):
  251. test = self.b1
  252. b = Book.objects.annotate(
  253. combined=ExpressionWrapper(
  254. F("pages") + F("rating"), output_field=IntegerField()
  255. )
  256. ).get(isbn=test.isbn)
  257. combined = int(test.pages + test.rating)
  258. self.assertEqual(b.combined, combined)
  259. def test_empty_expression_annotation(self):
  260. books = Book.objects.annotate(
  261. selected=ExpressionWrapper(Q(pk__in=[]), output_field=BooleanField())
  262. )
  263. self.assertEqual(len(books), Book.objects.count())
  264. self.assertTrue(all(not book.selected for book in books))
  265. books = Book.objects.annotate(
  266. selected=ExpressionWrapper(
  267. Q(pk__in=Book.objects.none()), output_field=BooleanField()
  268. )
  269. )
  270. self.assertEqual(len(books), Book.objects.count())
  271. self.assertTrue(all(not book.selected for book in books))
  272. def test_full_expression_annotation(self):
  273. books = Book.objects.annotate(
  274. selected=ExpressionWrapper(~Q(pk__in=[]), output_field=BooleanField()),
  275. )
  276. self.assertEqual(len(books), Book.objects.count())
  277. self.assertTrue(all(book.selected for book in books))
  278. def test_full_expression_wrapped_annotation(self):
  279. books = Book.objects.annotate(
  280. selected=Coalesce(~Q(pk__in=[]), True),
  281. )
  282. self.assertEqual(len(books), Book.objects.count())
  283. self.assertTrue(all(book.selected for book in books))
  284. def test_full_expression_annotation_with_aggregation(self):
  285. qs = Book.objects.filter(isbn="159059725").annotate(
  286. selected=ExpressionWrapper(~Q(pk__in=[]), output_field=BooleanField()),
  287. rating_count=Count("rating"),
  288. )
  289. self.assertEqual([book.rating_count for book in qs], [1])
  290. def test_aggregate_over_full_expression_annotation(self):
  291. qs = Book.objects.annotate(
  292. selected=ExpressionWrapper(~Q(pk__in=[]), output_field=BooleanField()),
  293. ).aggregate(selected__sum=Sum(Cast("selected", IntegerField())))
  294. self.assertEqual(qs["selected__sum"], Book.objects.count())
  295. def test_empty_queryset_annotation(self):
  296. qs = Author.objects.annotate(empty=Subquery(Author.objects.values("id").none()))
  297. self.assertIsNone(qs.first().empty)
  298. def test_annotate_with_aggregation(self):
  299. books = Book.objects.annotate(is_book=Value(1), rating_count=Count("rating"))
  300. for book in books:
  301. self.assertEqual(book.is_book, 1)
  302. self.assertEqual(book.rating_count, 1)
  303. def test_combined_expression_annotation_with_aggregation(self):
  304. book = Book.objects.annotate(
  305. combined=ExpressionWrapper(
  306. Value(3) * Value(4), output_field=IntegerField()
  307. ),
  308. rating_count=Count("rating"),
  309. ).first()
  310. self.assertEqual(book.combined, 12)
  311. self.assertEqual(book.rating_count, 1)
  312. def test_combined_f_expression_annotation_with_aggregation(self):
  313. book = (
  314. Book.objects.filter(isbn="159059725")
  315. .annotate(
  316. combined=ExpressionWrapper(
  317. F("price") * F("pages"), output_field=FloatField()
  318. ),
  319. rating_count=Count("rating"),
  320. )
  321. .first()
  322. )
  323. self.assertEqual(book.combined, 13410.0)
  324. self.assertEqual(book.rating_count, 1)
  325. @skipUnlessDBFeature("supports_boolean_expr_in_select_clause")
  326. def test_q_expression_annotation_with_aggregation(self):
  327. book = (
  328. Book.objects.filter(isbn="159059725")
  329. .annotate(
  330. isnull_pubdate=ExpressionWrapper(
  331. Q(pubdate__isnull=True),
  332. output_field=BooleanField(),
  333. ),
  334. rating_count=Count("rating"),
  335. )
  336. .first()
  337. )
  338. self.assertIs(book.isnull_pubdate, False)
  339. self.assertEqual(book.rating_count, 1)
  340. @skipUnlessDBFeature("supports_boolean_expr_in_select_clause")
  341. def test_grouping_by_q_expression_annotation(self):
  342. authors = (
  343. Author.objects.annotate(
  344. under_40=ExpressionWrapper(Q(age__lt=40), output_field=BooleanField()),
  345. )
  346. .values("under_40")
  347. .annotate(
  348. count_id=Count("id"),
  349. )
  350. .values("under_40", "count_id")
  351. )
  352. self.assertCountEqual(
  353. authors,
  354. [
  355. {"under_40": False, "count_id": 3},
  356. {"under_40": True, "count_id": 6},
  357. ],
  358. )
  359. def test_aggregate_over_annotation(self):
  360. agg = Author.objects.annotate(other_age=F("age")).aggregate(
  361. otherage_sum=Sum("other_age")
  362. )
  363. other_agg = Author.objects.aggregate(age_sum=Sum("age"))
  364. self.assertEqual(agg["otherage_sum"], other_agg["age_sum"])
  365. @skipUnlessDBFeature("can_distinct_on_fields")
  366. def test_distinct_on_with_annotation(self):
  367. store = Store.objects.create(
  368. name="test store",
  369. original_opening=datetime.datetime.now(),
  370. friday_night_closing=datetime.time(21, 00, 00),
  371. )
  372. names = [
  373. "Theodore Roosevelt",
  374. "Eleanor Roosevelt",
  375. "Franklin Roosevelt",
  376. "Ned Stark",
  377. "Catelyn Stark",
  378. ]
  379. for name in names:
  380. Employee.objects.create(
  381. store=store,
  382. first_name=name.split()[0],
  383. last_name=name.split()[1],
  384. age=30,
  385. salary=2000,
  386. )
  387. people = Employee.objects.annotate(
  388. name_lower=Lower("last_name"),
  389. ).distinct("name_lower")
  390. self.assertEqual({p.last_name for p in people}, {"Stark", "Roosevelt"})
  391. self.assertEqual(len(people), 2)
  392. people2 = Employee.objects.annotate(
  393. test_alias=F("store__name"),
  394. ).distinct("test_alias")
  395. self.assertEqual(len(people2), 1)
  396. lengths = (
  397. Employee.objects.annotate(
  398. name_len=Length("first_name"),
  399. )
  400. .distinct("name_len")
  401. .values_list("name_len", flat=True)
  402. )
  403. self.assertCountEqual(lengths, [3, 7, 8])
  404. def test_filter_annotation(self):
  405. books = Book.objects.annotate(is_book=Value(1)).filter(is_book=1)
  406. for book in books:
  407. self.assertEqual(book.is_book, 1)
  408. def test_filter_annotation_with_f(self):
  409. books = Book.objects.annotate(other_rating=F("rating")).filter(other_rating=3.5)
  410. for book in books:
  411. self.assertEqual(book.other_rating, 3.5)
  412. def test_filter_annotation_with_double_f(self):
  413. books = Book.objects.annotate(other_rating=F("rating")).filter(
  414. other_rating=F("rating")
  415. )
  416. for book in books:
  417. self.assertEqual(book.other_rating, book.rating)
  418. def test_filter_agg_with_double_f(self):
  419. books = Book.objects.annotate(sum_rating=Sum("rating")).filter(
  420. sum_rating=F("sum_rating")
  421. )
  422. for book in books:
  423. self.assertEqual(book.sum_rating, book.rating)
  424. def test_filter_wrong_annotation(self):
  425. with self.assertRaisesMessage(
  426. FieldError, "Cannot resolve keyword 'nope' into field."
  427. ):
  428. list(
  429. Book.objects.annotate(sum_rating=Sum("rating")).filter(
  430. sum_rating=F("nope")
  431. )
  432. )
  433. def test_values_wrong_annotation(self):
  434. expected_message = (
  435. "Cannot resolve keyword 'annotation_typo' into field. Choices are: %s"
  436. )
  437. article_fields = ", ".join(
  438. ["annotation"] + sorted(get_field_names_from_opts(Book._meta))
  439. )
  440. with self.assertRaisesMessage(FieldError, expected_message % article_fields):
  441. Book.objects.annotate(annotation=Value(1)).values_list("annotation_typo")
  442. def test_decimal_annotation(self):
  443. salary = Decimal(10) ** -Employee._meta.get_field("salary").decimal_places
  444. Employee.objects.create(
  445. first_name="Max",
  446. last_name="Paine",
  447. store=Store.objects.first(),
  448. age=23,
  449. salary=salary,
  450. )
  451. self.assertEqual(
  452. Employee.objects.annotate(new_salary=F("salary") / 10).get().new_salary,
  453. salary / 10,
  454. )
  455. def test_filter_decimal_annotation(self):
  456. qs = (
  457. Book.objects.annotate(new_price=F("price") + 1)
  458. .filter(new_price=Decimal(31))
  459. .values_list("new_price")
  460. )
  461. self.assertEqual(qs.get(), (Decimal(31),))
  462. def test_combined_annotation_commutative(self):
  463. book1 = Book.objects.annotate(adjusted_rating=F("rating") + 2).get(
  464. pk=self.b1.pk
  465. )
  466. book2 = Book.objects.annotate(adjusted_rating=2 + F("rating")).get(
  467. pk=self.b1.pk
  468. )
  469. self.assertEqual(book1.adjusted_rating, book2.adjusted_rating)
  470. book1 = Book.objects.annotate(adjusted_rating=F("rating") + None).get(
  471. pk=self.b1.pk
  472. )
  473. book2 = Book.objects.annotate(adjusted_rating=None + F("rating")).get(
  474. pk=self.b1.pk
  475. )
  476. self.assertIs(book1.adjusted_rating, None)
  477. self.assertEqual(book1.adjusted_rating, book2.adjusted_rating)
  478. def test_update_with_annotation(self):
  479. book_preupdate = Book.objects.get(pk=self.b2.pk)
  480. Book.objects.annotate(other_rating=F("rating") - 1).update(
  481. rating=F("other_rating")
  482. )
  483. book_postupdate = Book.objects.get(pk=self.b2.pk)
  484. self.assertEqual(book_preupdate.rating - 1, book_postupdate.rating)
  485. def test_annotation_with_m2m(self):
  486. books = (
  487. Book.objects.annotate(author_age=F("authors__age"))
  488. .filter(pk=self.b1.pk)
  489. .order_by("author_age")
  490. )
  491. self.assertEqual(books[0].author_age, 34)
  492. self.assertEqual(books[1].author_age, 35)
  493. def test_annotation_reverse_m2m(self):
  494. books = (
  495. Book.objects.annotate(
  496. store_name=F("store__name"),
  497. )
  498. .filter(
  499. name="Practical Django Projects",
  500. )
  501. .order_by("store_name")
  502. )
  503. self.assertQuerySetEqual(
  504. books,
  505. ["Amazon.com", "Books.com", "Mamma and Pappa's Books"],
  506. lambda b: b.store_name,
  507. )
  508. def test_values_annotation(self):
  509. """
  510. Annotations can reference fields in a values clause,
  511. and contribute to an existing values clause.
  512. """
  513. # annotate references a field in values()
  514. qs = Book.objects.values("rating").annotate(other_rating=F("rating") - 1)
  515. book = qs.get(pk=self.b1.pk)
  516. self.assertEqual(book["rating"] - 1, book["other_rating"])
  517. # filter refs the annotated value
  518. book = qs.get(other_rating=4)
  519. self.assertEqual(book["other_rating"], 4)
  520. # can annotate an existing values with a new field
  521. book = qs.annotate(other_isbn=F("isbn")).get(other_rating=4)
  522. self.assertEqual(book["other_rating"], 4)
  523. self.assertEqual(book["other_isbn"], "155860191")
  524. def test_values_fields_annotations_order(self):
  525. qs = Book.objects.annotate(other_rating=F("rating") - 1).values(
  526. "other_rating", "rating"
  527. )
  528. book = qs.get(pk=self.b1.pk)
  529. self.assertEqual(
  530. list(book.items()),
  531. [("other_rating", self.b1.rating - 1), ("rating", self.b1.rating)],
  532. )
  533. def test_values_with_pk_annotation(self):
  534. # annotate references a field in values() with pk
  535. publishers = Publisher.objects.values("id", "book__rating").annotate(
  536. total=Sum("book__rating")
  537. )
  538. for publisher in publishers.filter(pk=self.p1.pk):
  539. self.assertEqual(publisher["book__rating"], publisher["total"])
  540. def test_defer_annotation(self):
  541. """
  542. Deferred attributes can be referenced by an annotation,
  543. but they are not themselves deferred, and cannot be deferred.
  544. """
  545. qs = Book.objects.defer("rating").annotate(other_rating=F("rating") - 1)
  546. with self.assertNumQueries(2):
  547. book = qs.get(other_rating=4)
  548. self.assertEqual(book.rating, 5)
  549. self.assertEqual(book.other_rating, 4)
  550. with self.assertRaisesMessage(
  551. FieldDoesNotExist, "Book has no field named 'other_rating'"
  552. ):
  553. book = qs.defer("other_rating").get(other_rating=4)
  554. def test_mti_annotations(self):
  555. """
  556. Fields on an inherited model can be referenced by an
  557. annotated field.
  558. """
  559. d = DepartmentStore.objects.create(
  560. name="Angus & Robinson",
  561. original_opening=datetime.date(2014, 3, 8),
  562. friday_night_closing=datetime.time(21, 00, 00),
  563. chain="Westfield",
  564. )
  565. books = Book.objects.filter(rating__gt=4)
  566. for b in books:
  567. d.books.add(b)
  568. qs = (
  569. DepartmentStore.objects.annotate(
  570. other_name=F("name"),
  571. other_chain=F("chain"),
  572. is_open=Value(True, BooleanField()),
  573. book_isbn=F("books__isbn"),
  574. )
  575. .order_by("book_isbn")
  576. .filter(chain="Westfield")
  577. )
  578. self.assertQuerySetEqual(
  579. qs,
  580. [
  581. ("Angus & Robinson", "Westfield", True, "155860191"),
  582. ("Angus & Robinson", "Westfield", True, "159059725"),
  583. ],
  584. lambda d: (d.other_name, d.other_chain, d.is_open, d.book_isbn),
  585. )
  586. def test_null_annotation(self):
  587. """
  588. Annotating None onto a model round-trips
  589. """
  590. book = Book.objects.annotate(
  591. no_value=Value(None, output_field=IntegerField())
  592. ).first()
  593. self.assertIsNone(book.no_value)
  594. def test_order_by_annotation(self):
  595. authors = Author.objects.annotate(other_age=F("age")).order_by("other_age")
  596. self.assertQuerySetEqual(
  597. authors,
  598. [
  599. 25,
  600. 29,
  601. 29,
  602. 34,
  603. 35,
  604. 37,
  605. 45,
  606. 46,
  607. 57,
  608. ],
  609. lambda a: a.other_age,
  610. )
  611. def test_order_by_aggregate(self):
  612. authors = (
  613. Author.objects.values("age")
  614. .annotate(age_count=Count("age"))
  615. .order_by("age_count", "age")
  616. )
  617. self.assertQuerySetEqual(
  618. authors,
  619. [
  620. (25, 1),
  621. (34, 1),
  622. (35, 1),
  623. (37, 1),
  624. (45, 1),
  625. (46, 1),
  626. (57, 1),
  627. (29, 2),
  628. ],
  629. lambda a: (a["age"], a["age_count"]),
  630. )
  631. def test_raw_sql_with_inherited_field(self):
  632. DepartmentStore.objects.create(
  633. name="Angus & Robinson",
  634. original_opening=datetime.date(2014, 3, 8),
  635. friday_night_closing=datetime.time(21),
  636. chain="Westfield",
  637. area=123,
  638. )
  639. tests = (
  640. ("name", "Angus & Robinson"),
  641. ("surface", 123),
  642. ("case when name='Angus & Robinson' then chain else name end", "Westfield"),
  643. )
  644. for sql, expected_result in tests:
  645. with self.subTest(sql=sql):
  646. self.assertSequenceEqual(
  647. DepartmentStore.objects.annotate(
  648. annotation=RawSQL(sql, ()),
  649. ).values_list("annotation", flat=True),
  650. [expected_result],
  651. )
  652. def test_annotate_exists(self):
  653. authors = Author.objects.annotate(c=Count("id")).filter(c__gt=1)
  654. self.assertFalse(authors.exists())
  655. def test_column_field_ordering(self):
  656. """
  657. Columns are aligned in the correct order for resolve_columns. This test
  658. will fail on MySQL if column ordering is out. Column fields should be
  659. aligned as:
  660. 1. extra_select
  661. 2. model_fields
  662. 3. annotation_fields
  663. 4. model_related_fields
  664. """
  665. store = Store.objects.first()
  666. Employee.objects.create(
  667. id=1,
  668. first_name="Max",
  669. manager=True,
  670. last_name="Paine",
  671. store=store,
  672. age=23,
  673. salary=Decimal(50000.00),
  674. )
  675. Employee.objects.create(
  676. id=2,
  677. first_name="Buffy",
  678. manager=False,
  679. last_name="Summers",
  680. store=store,
  681. age=18,
  682. salary=Decimal(40000.00),
  683. )
  684. qs = (
  685. Employee.objects.extra(select={"random_value": "42"})
  686. .select_related("store")
  687. .annotate(
  688. annotated_value=Value(17),
  689. )
  690. )
  691. rows = [
  692. (1, "Max", True, 42, "Paine", 23, Decimal(50000.00), store.name, 17),
  693. (2, "Buffy", False, 42, "Summers", 18, Decimal(40000.00), store.name, 17),
  694. ]
  695. self.assertQuerySetEqual(
  696. qs.order_by("id"),
  697. rows,
  698. lambda e: (
  699. e.id,
  700. e.first_name,
  701. e.manager,
  702. e.random_value,
  703. e.last_name,
  704. e.age,
  705. e.salary,
  706. e.store.name,
  707. e.annotated_value,
  708. ),
  709. )
  710. def test_column_field_ordering_with_deferred(self):
  711. store = Store.objects.first()
  712. Employee.objects.create(
  713. id=1,
  714. first_name="Max",
  715. manager=True,
  716. last_name="Paine",
  717. store=store,
  718. age=23,
  719. salary=Decimal(50000.00),
  720. )
  721. Employee.objects.create(
  722. id=2,
  723. first_name="Buffy",
  724. manager=False,
  725. last_name="Summers",
  726. store=store,
  727. age=18,
  728. salary=Decimal(40000.00),
  729. )
  730. qs = (
  731. Employee.objects.extra(select={"random_value": "42"})
  732. .select_related("store")
  733. .annotate(
  734. annotated_value=Value(17),
  735. )
  736. )
  737. rows = [
  738. (1, "Max", True, 42, "Paine", 23, Decimal(50000.00), store.name, 17),
  739. (2, "Buffy", False, 42, "Summers", 18, Decimal(40000.00), store.name, 17),
  740. ]
  741. # and we respect deferred columns!
  742. self.assertQuerySetEqual(
  743. qs.defer("age").order_by("id"),
  744. rows,
  745. lambda e: (
  746. e.id,
  747. e.first_name,
  748. e.manager,
  749. e.random_value,
  750. e.last_name,
  751. e.age,
  752. e.salary,
  753. e.store.name,
  754. e.annotated_value,
  755. ),
  756. )
  757. def test_custom_functions(self):
  758. Company(
  759. name="Apple",
  760. motto=None,
  761. ticker_name="APPL",
  762. description="Beautiful Devices",
  763. ).save()
  764. Company(
  765. name="Django Software Foundation",
  766. motto=None,
  767. ticker_name=None,
  768. description=None,
  769. ).save()
  770. Company(
  771. name="Google",
  772. motto="Do No Evil",
  773. ticker_name="GOOG",
  774. description="Internet Company",
  775. ).save()
  776. Company(
  777. name="Yahoo", motto=None, ticker_name=None, description="Internet Company"
  778. ).save()
  779. qs = Company.objects.annotate(
  780. tagline=Func(
  781. F("motto"),
  782. F("ticker_name"),
  783. F("description"),
  784. Value("No Tag"),
  785. function="COALESCE",
  786. )
  787. ).order_by("name")
  788. self.assertQuerySetEqual(
  789. qs,
  790. [
  791. ("Apple", "APPL"),
  792. ("Django Software Foundation", "No Tag"),
  793. ("Google", "Do No Evil"),
  794. ("Yahoo", "Internet Company"),
  795. ],
  796. lambda c: (c.name, c.tagline),
  797. )
  798. def test_custom_functions_can_ref_other_functions(self):
  799. Company(
  800. name="Apple",
  801. motto=None,
  802. ticker_name="APPL",
  803. description="Beautiful Devices",
  804. ).save()
  805. Company(
  806. name="Django Software Foundation",
  807. motto=None,
  808. ticker_name=None,
  809. description=None,
  810. ).save()
  811. Company(
  812. name="Google",
  813. motto="Do No Evil",
  814. ticker_name="GOOG",
  815. description="Internet Company",
  816. ).save()
  817. Company(
  818. name="Yahoo", motto=None, ticker_name=None, description="Internet Company"
  819. ).save()
  820. class Lower(Func):
  821. function = "LOWER"
  822. qs = (
  823. Company.objects.annotate(
  824. tagline=Func(
  825. F("motto"),
  826. F("ticker_name"),
  827. F("description"),
  828. Value("No Tag"),
  829. function="COALESCE",
  830. )
  831. )
  832. .annotate(
  833. tagline_lower=Lower(F("tagline")),
  834. )
  835. .order_by("name")
  836. )
  837. # LOWER function supported by:
  838. # oracle, postgres, mysql, sqlite, sqlserver
  839. self.assertQuerySetEqual(
  840. qs,
  841. [
  842. ("Apple", "APPL".lower()),
  843. ("Django Software Foundation", "No Tag".lower()),
  844. ("Google", "Do No Evil".lower()),
  845. ("Yahoo", "Internet Company".lower()),
  846. ],
  847. lambda c: (c.name, c.tagline_lower),
  848. )
  849. def test_boolean_value_annotation(self):
  850. books = Book.objects.annotate(
  851. is_book=Value(True, output_field=BooleanField()),
  852. is_pony=Value(False, output_field=BooleanField()),
  853. is_none=Value(None, output_field=BooleanField(null=True)),
  854. )
  855. self.assertGreater(len(books), 0)
  856. for book in books:
  857. self.assertIs(book.is_book, True)
  858. self.assertIs(book.is_pony, False)
  859. self.assertIsNone(book.is_none)
  860. def test_annotation_in_f_grouped_by_annotation(self):
  861. qs = (
  862. Publisher.objects.annotate(multiplier=Value(3))
  863. # group by option => sum of value * multiplier
  864. .values("name")
  865. .annotate(multiplied_value_sum=Sum(F("multiplier") * F("num_awards")))
  866. .order_by()
  867. )
  868. self.assertCountEqual(
  869. qs,
  870. [
  871. {"multiplied_value_sum": 9, "name": "Apress"},
  872. {"multiplied_value_sum": 0, "name": "Jonno's House of Books"},
  873. {"multiplied_value_sum": 27, "name": "Morgan Kaufmann"},
  874. {"multiplied_value_sum": 21, "name": "Prentice Hall"},
  875. {"multiplied_value_sum": 3, "name": "Sams"},
  876. ],
  877. )
  878. def test_arguments_must_be_expressions(self):
  879. msg = "QuerySet.annotate() received non-expression(s): %s."
  880. with self.assertRaisesMessage(TypeError, msg % BooleanField()):
  881. Book.objects.annotate(BooleanField())
  882. with self.assertRaisesMessage(TypeError, msg % True):
  883. Book.objects.annotate(is_book=True)
  884. with self.assertRaisesMessage(
  885. TypeError, msg % ", ".join([str(BooleanField()), "True"])
  886. ):
  887. Book.objects.annotate(BooleanField(), Value(False), is_book=True)
  888. def test_chaining_annotation_filter_with_m2m(self):
  889. qs = (
  890. Author.objects.filter(
  891. name="Adrian Holovaty",
  892. friends__age=35,
  893. )
  894. .annotate(
  895. jacob_name=F("friends__name"),
  896. )
  897. .filter(
  898. friends__age=29,
  899. )
  900. .annotate(
  901. james_name=F("friends__name"),
  902. )
  903. .values("jacob_name", "james_name")
  904. )
  905. self.assertCountEqual(
  906. qs,
  907. [{"jacob_name": "Jacob Kaplan-Moss", "james_name": "James Bennett"}],
  908. )
  909. def test_annotation_filter_with_subquery(self):
  910. long_books_qs = (
  911. Book.objects.filter(
  912. publisher=OuterRef("pk"),
  913. pages__gt=400,
  914. )
  915. .values("publisher")
  916. .annotate(count=Count("pk"))
  917. .values("count")
  918. )
  919. publisher_books_qs = (
  920. Publisher.objects.annotate(
  921. total_books=Count("book"),
  922. )
  923. .filter(
  924. total_books=Subquery(long_books_qs, output_field=IntegerField()),
  925. )
  926. .values("name")
  927. )
  928. self.assertCountEqual(
  929. publisher_books_qs, [{"name": "Sams"}, {"name": "Morgan Kaufmann"}]
  930. )
  931. def test_annotation_and_alias_filter_in_subquery(self):
  932. awarded_publishers_qs = (
  933. Publisher.objects.filter(num_awards__gt=4)
  934. .annotate(publisher_annotate=Value(1))
  935. .alias(publisher_alias=Value(1))
  936. )
  937. qs = Publisher.objects.filter(pk__in=awarded_publishers_qs)
  938. self.assertCountEqual(qs, [self.p3, self.p4])
  939. def test_annotation_and_alias_filter_related_in_subquery(self):
  940. long_books_qs = (
  941. Book.objects.filter(pages__gt=400)
  942. .annotate(book_annotate=Value(1))
  943. .alias(book_alias=Value(1))
  944. )
  945. publisher_books_qs = Publisher.objects.filter(
  946. book__in=long_books_qs,
  947. ).values("name")
  948. self.assertCountEqual(
  949. publisher_books_qs,
  950. [
  951. {"name": "Apress"},
  952. {"name": "Sams"},
  953. {"name": "Prentice Hall"},
  954. {"name": "Morgan Kaufmann"},
  955. ],
  956. )
  957. def test_annotation_exists_none_query(self):
  958. self.assertIs(
  959. Author.objects.annotate(exists=Exists(Company.objects.none()))
  960. .get(pk=self.a1.pk)
  961. .exists,
  962. False,
  963. )
  964. def test_annotation_exists_aggregate_values_chaining(self):
  965. qs = (
  966. Book.objects.values("publisher")
  967. .annotate(
  968. has_authors=Exists(
  969. Book.authors.through.objects.filter(book=OuterRef("pk"))
  970. ),
  971. max_pubdate=Max("pubdate"),
  972. )
  973. .values_list("max_pubdate", flat=True)
  974. .order_by("max_pubdate")
  975. )
  976. self.assertCountEqual(
  977. qs,
  978. [
  979. datetime.date(1991, 10, 15),
  980. datetime.date(2008, 3, 3),
  981. datetime.date(2008, 6, 23),
  982. datetime.date(2008, 11, 3),
  983. ],
  984. )
  985. @skipUnlessDBFeature("supports_subqueries_in_group_by")
  986. def test_annotation_subquery_and_aggregate_values_chaining(self):
  987. qs = (
  988. Book.objects.annotate(pub_year=ExtractYear("pubdate"))
  989. .values("pub_year")
  990. .annotate(
  991. top_rating=Subquery(
  992. Book.objects.filter(pubdate__year=OuterRef("pub_year"))
  993. .order_by("-rating")
  994. .values("rating")[:1]
  995. ),
  996. total_pages=Sum("pages"),
  997. )
  998. .values("pub_year", "total_pages", "top_rating")
  999. )
  1000. self.assertCountEqual(
  1001. qs,
  1002. [
  1003. {"pub_year": 1991, "top_rating": 5.0, "total_pages": 946},
  1004. {"pub_year": 1995, "top_rating": 4.0, "total_pages": 1132},
  1005. {"pub_year": 2007, "top_rating": 4.5, "total_pages": 447},
  1006. {"pub_year": 2008, "top_rating": 4.0, "total_pages": 1178},
  1007. ],
  1008. )
  1009. def test_annotation_subquery_outerref_transform(self):
  1010. qs = Book.objects.annotate(
  1011. top_rating_year=Subquery(
  1012. Book.objects.filter(pubdate__year=OuterRef("pubdate__year"))
  1013. .order_by("-rating")
  1014. .values("rating")[:1]
  1015. ),
  1016. ).values("pubdate__year", "top_rating_year")
  1017. self.assertCountEqual(
  1018. qs,
  1019. [
  1020. {"pubdate__year": 1991, "top_rating_year": 5.0},
  1021. {"pubdate__year": 1995, "top_rating_year": 4.0},
  1022. {"pubdate__year": 2007, "top_rating_year": 4.5},
  1023. {"pubdate__year": 2008, "top_rating_year": 4.0},
  1024. {"pubdate__year": 2008, "top_rating_year": 4.0},
  1025. {"pubdate__year": 2008, "top_rating_year": 4.0},
  1026. ],
  1027. )
  1028. def test_annotation_aggregate_with_m2o(self):
  1029. qs = (
  1030. Author.objects.filter(age__lt=30)
  1031. .annotate(
  1032. max_pages=Case(
  1033. When(book_contact_set__isnull=True, then=Value(0)),
  1034. default=Max(F("book__pages")),
  1035. ),
  1036. )
  1037. .values("name", "max_pages")
  1038. )
  1039. self.assertCountEqual(
  1040. qs,
  1041. [
  1042. {"name": "James Bennett", "max_pages": 300},
  1043. {"name": "Paul Bissex", "max_pages": 0},
  1044. {"name": "Wesley J. Chun", "max_pages": 0},
  1045. ],
  1046. )
  1047. def test_alias_sql_injection(self):
  1048. crafted_alias = """injected_name" from "annotations_book"; --"""
  1049. msg = (
  1050. "Column aliases cannot contain whitespace characters, quotation marks, "
  1051. "semicolons, or SQL comments."
  1052. )
  1053. with self.assertRaisesMessage(ValueError, msg):
  1054. Book.objects.annotate(**{crafted_alias: Value(1)})
  1055. def test_alias_forbidden_chars(self):
  1056. tests = [
  1057. 'al"ias',
  1058. "a'lias",
  1059. "ali`as",
  1060. "alia s",
  1061. "alias\t",
  1062. "ali\nas",
  1063. "alias--",
  1064. "ali/*as",
  1065. "alias*/",
  1066. "alias;",
  1067. # [] are used by MSSQL.
  1068. "alias[",
  1069. "alias]",
  1070. ]
  1071. msg = (
  1072. "Column aliases cannot contain whitespace characters, quotation marks, "
  1073. "semicolons, or SQL comments."
  1074. )
  1075. for crafted_alias in tests:
  1076. with self.subTest(crafted_alias):
  1077. with self.assertRaisesMessage(ValueError, msg):
  1078. Book.objects.annotate(**{crafted_alias: Value(1)})
  1079. @skipUnless(connection.vendor == "postgresql", "PostgreSQL tests")
  1080. @skipUnlessDBFeature("supports_json_field")
  1081. def test_set_returning_functions(self):
  1082. class JSONBPathQuery(Func):
  1083. function = "jsonb_path_query"
  1084. output_field = JSONField()
  1085. set_returning = True
  1086. test_model = JsonModel.objects.create(
  1087. data={"key": [{"id": 1, "name": "test1"}, {"id": 2, "name": "test2"}]}, id=1
  1088. )
  1089. qs = JsonModel.objects.annotate(
  1090. table_element=JSONBPathQuery("data", Value("$.key[*]"))
  1091. ).filter(pk=test_model.pk)
  1092. self.assertEqual(qs.count(), len(qs))
  1093. class AliasTests(TestCase):
  1094. @classmethod
  1095. def setUpTestData(cls):
  1096. cls.a1 = Author.objects.create(name="Adrian Holovaty", age=34)
  1097. cls.a2 = Author.objects.create(name="Jacob Kaplan-Moss", age=35)
  1098. cls.a3 = Author.objects.create(name="James Bennett", age=34)
  1099. cls.a4 = Author.objects.create(name="Peter Norvig", age=57)
  1100. cls.a5 = Author.objects.create(name="Stuart Russell", age=46)
  1101. p1 = Publisher.objects.create(name="Apress", num_awards=3)
  1102. cls.b1 = Book.objects.create(
  1103. isbn="159059725",
  1104. pages=447,
  1105. rating=4.5,
  1106. price=Decimal("30.00"),
  1107. contact=cls.a1,
  1108. publisher=p1,
  1109. pubdate=datetime.date(2007, 12, 6),
  1110. name="The Definitive Guide to Django: Web Development Done Right",
  1111. )
  1112. cls.b2 = Book.objects.create(
  1113. isbn="159059996",
  1114. pages=300,
  1115. rating=4.0,
  1116. price=Decimal("29.69"),
  1117. contact=cls.a3,
  1118. publisher=p1,
  1119. pubdate=datetime.date(2008, 6, 23),
  1120. name="Practical Django Projects",
  1121. )
  1122. cls.b3 = Book.objects.create(
  1123. isbn="013790395",
  1124. pages=1132,
  1125. rating=4.0,
  1126. price=Decimal("82.80"),
  1127. contact=cls.a4,
  1128. publisher=p1,
  1129. pubdate=datetime.date(1995, 1, 15),
  1130. name="Artificial Intelligence: A Modern Approach",
  1131. )
  1132. cls.b4 = Book.objects.create(
  1133. isbn="155860191",
  1134. pages=946,
  1135. rating=5.0,
  1136. price=Decimal("75.00"),
  1137. contact=cls.a4,
  1138. publisher=p1,
  1139. pubdate=datetime.date(1991, 10, 15),
  1140. name=(
  1141. "Paradigms of Artificial Intelligence Programming: Case Studies in "
  1142. "Common Lisp"
  1143. ),
  1144. )
  1145. cls.b1.authors.add(cls.a1, cls.a2)
  1146. cls.b2.authors.add(cls.a3)
  1147. cls.b3.authors.add(cls.a4, cls.a5)
  1148. cls.b4.authors.add(cls.a4)
  1149. Store.objects.create(
  1150. name="Amazon.com",
  1151. original_opening=datetime.datetime(1994, 4, 23, 9, 17, 42),
  1152. friday_night_closing=datetime.time(23, 59, 59),
  1153. )
  1154. Store.objects.create(
  1155. name="Books.com",
  1156. original_opening=datetime.datetime(2001, 3, 15, 11, 23, 37),
  1157. friday_night_closing=datetime.time(23, 59, 59),
  1158. )
  1159. def test_basic_alias(self):
  1160. qs = Book.objects.alias(is_book=Value(1))
  1161. self.assertIs(hasattr(qs.first(), "is_book"), False)
  1162. def test_basic_alias_annotation(self):
  1163. qs = Book.objects.alias(
  1164. is_book_alias=Value(1),
  1165. ).annotate(is_book=F("is_book_alias"))
  1166. self.assertIs(hasattr(qs.first(), "is_book_alias"), False)
  1167. for book in qs:
  1168. with self.subTest(book=book):
  1169. self.assertEqual(book.is_book, 1)
  1170. def test_basic_alias_f_annotation(self):
  1171. qs = Book.objects.alias(another_rating_alias=F("rating")).annotate(
  1172. another_rating=F("another_rating_alias")
  1173. )
  1174. self.assertIs(hasattr(qs.first(), "another_rating_alias"), False)
  1175. for book in qs:
  1176. with self.subTest(book=book):
  1177. self.assertEqual(book.another_rating, book.rating)
  1178. def test_basic_alias_f_transform_annotation(self):
  1179. qs = Book.objects.alias(
  1180. pubdate_alias=F("pubdate"),
  1181. ).annotate(pubdate_year=F("pubdate_alias__year"))
  1182. self.assertIs(hasattr(qs.first(), "pubdate_alias"), False)
  1183. for book in qs:
  1184. with self.subTest(book=book):
  1185. self.assertEqual(book.pubdate_year, book.pubdate.year)
  1186. def test_alias_after_annotation(self):
  1187. qs = Book.objects.annotate(
  1188. is_book=Value(1),
  1189. ).alias(is_book_alias=F("is_book"))
  1190. book = qs.first()
  1191. self.assertIs(hasattr(book, "is_book"), True)
  1192. self.assertIs(hasattr(book, "is_book_alias"), False)
  1193. def test_overwrite_annotation_with_alias(self):
  1194. qs = Book.objects.annotate(is_book=Value(1)).alias(is_book=F("is_book"))
  1195. self.assertIs(hasattr(qs.first(), "is_book"), False)
  1196. def test_overwrite_alias_with_annotation(self):
  1197. qs = Book.objects.alias(is_book=Value(1)).annotate(is_book=F("is_book"))
  1198. for book in qs:
  1199. with self.subTest(book=book):
  1200. self.assertEqual(book.is_book, 1)
  1201. def test_alias_annotation_expression(self):
  1202. qs = Book.objects.alias(
  1203. is_book_alias=Value(1),
  1204. ).annotate(is_book=Coalesce("is_book_alias", 0))
  1205. self.assertIs(hasattr(qs.first(), "is_book_alias"), False)
  1206. for book in qs:
  1207. with self.subTest(book=book):
  1208. self.assertEqual(book.is_book, 1)
  1209. def test_alias_default_alias_expression(self):
  1210. qs = Author.objects.alias(
  1211. Sum("book__pages"),
  1212. ).filter(book__pages__sum__gt=2000)
  1213. self.assertIs(hasattr(qs.first(), "book__pages__sum"), False)
  1214. self.assertSequenceEqual(qs, [self.a4])
  1215. def test_joined_alias_annotation(self):
  1216. qs = (
  1217. Book.objects.select_related("publisher")
  1218. .alias(
  1219. num_awards_alias=F("publisher__num_awards"),
  1220. )
  1221. .annotate(num_awards=F("num_awards_alias"))
  1222. )
  1223. self.assertIs(hasattr(qs.first(), "num_awards_alias"), False)
  1224. for book in qs:
  1225. with self.subTest(book=book):
  1226. self.assertEqual(book.num_awards, book.publisher.num_awards)
  1227. def test_alias_annotate_with_aggregation(self):
  1228. qs = Book.objects.alias(
  1229. is_book_alias=Value(1),
  1230. rating_count_alias=Count("rating"),
  1231. ).annotate(
  1232. is_book=F("is_book_alias"),
  1233. rating_count=F("rating_count_alias"),
  1234. )
  1235. book = qs.first()
  1236. self.assertIs(hasattr(book, "is_book_alias"), False)
  1237. self.assertIs(hasattr(book, "rating_count_alias"), False)
  1238. for book in qs:
  1239. with self.subTest(book=book):
  1240. self.assertEqual(book.is_book, 1)
  1241. self.assertEqual(book.rating_count, 1)
  1242. def test_filter_alias_with_f(self):
  1243. qs = Book.objects.alias(
  1244. other_rating=F("rating"),
  1245. ).filter(other_rating=4.5)
  1246. self.assertIs(hasattr(qs.first(), "other_rating"), False)
  1247. self.assertSequenceEqual(qs, [self.b1])
  1248. def test_filter_alias_with_double_f(self):
  1249. qs = Book.objects.alias(
  1250. other_rating=F("rating"),
  1251. ).filter(other_rating=F("rating"))
  1252. self.assertIs(hasattr(qs.first(), "other_rating"), False)
  1253. self.assertEqual(qs.count(), Book.objects.count())
  1254. def test_filter_alias_agg_with_double_f(self):
  1255. qs = Book.objects.alias(
  1256. sum_rating=Sum("rating"),
  1257. ).filter(sum_rating=F("sum_rating"))
  1258. self.assertIs(hasattr(qs.first(), "sum_rating"), False)
  1259. self.assertEqual(qs.count(), Book.objects.count())
  1260. def test_update_with_alias(self):
  1261. Book.objects.alias(
  1262. other_rating=F("rating") - 1,
  1263. ).update(rating=F("other_rating"))
  1264. self.b1.refresh_from_db()
  1265. self.assertEqual(self.b1.rating, 3.5)
  1266. def test_order_by_alias(self):
  1267. qs = Author.objects.alias(other_age=F("age")).order_by("other_age")
  1268. self.assertIs(hasattr(qs.first(), "other_age"), False)
  1269. self.assertQuerySetEqual(qs, [34, 34, 35, 46, 57], lambda a: a.age)
  1270. def test_order_by_alias_aggregate(self):
  1271. qs = (
  1272. Author.objects.values("age")
  1273. .alias(age_count=Count("age"))
  1274. .order_by("age_count", "age")
  1275. )
  1276. self.assertIs(hasattr(qs.first(), "age_count"), False)
  1277. self.assertQuerySetEqual(qs, [35, 46, 57, 34], lambda a: a["age"])
  1278. def test_dates_alias(self):
  1279. qs = Book.objects.alias(
  1280. pubdate_alias=F("pubdate"),
  1281. ).dates("pubdate_alias", "month")
  1282. self.assertCountEqual(
  1283. qs,
  1284. [
  1285. datetime.date(1991, 10, 1),
  1286. datetime.date(1995, 1, 1),
  1287. datetime.date(2007, 12, 1),
  1288. datetime.date(2008, 6, 1),
  1289. ],
  1290. )
  1291. def test_datetimes_alias(self):
  1292. qs = Store.objects.alias(
  1293. original_opening_alias=F("original_opening"),
  1294. ).datetimes("original_opening_alias", "year")
  1295. self.assertCountEqual(
  1296. qs,
  1297. [
  1298. datetime.datetime(1994, 1, 1),
  1299. datetime.datetime(2001, 1, 1),
  1300. ],
  1301. )
  1302. def test_aggregate_alias(self):
  1303. msg = (
  1304. "Cannot aggregate over the 'other_age' alias. Use annotate() to promote it."
  1305. )
  1306. with self.assertRaisesMessage(FieldError, msg):
  1307. Author.objects.alias(
  1308. other_age=F("age"),
  1309. ).aggregate(otherage_sum=Sum("other_age"))
  1310. def test_defer_only_alias(self):
  1311. qs = Book.objects.alias(rating_alias=F("rating") - 1)
  1312. msg = "Book has no field named 'rating_alias'"
  1313. for operation in ["defer", "only"]:
  1314. with self.subTest(operation=operation):
  1315. with self.assertRaisesMessage(FieldDoesNotExist, msg):
  1316. getattr(qs, operation)("rating_alias").first()
  1317. @skipUnlessDBFeature("can_distinct_on_fields")
  1318. def test_distinct_on_alias(self):
  1319. qs = Book.objects.alias(rating_alias=F("rating") - 1)
  1320. msg = "Cannot resolve keyword 'rating_alias' into field."
  1321. with self.assertRaisesMessage(FieldError, msg):
  1322. qs.distinct("rating_alias").first()
  1323. def test_values_alias(self):
  1324. qs = Book.objects.alias(rating_alias=F("rating") - 1)
  1325. msg = "Cannot select the 'rating_alias' alias. Use annotate() to promote it."
  1326. for operation in ["values", "values_list"]:
  1327. with self.subTest(operation=operation):
  1328. with self.assertRaisesMessage(FieldError, msg):
  1329. getattr(qs, operation)("rating_alias")
  1330. def test_alias_sql_injection(self):
  1331. crafted_alias = """injected_name" from "annotations_book"; --"""
  1332. msg = (
  1333. "Column aliases cannot contain whitespace characters, quotation marks, "
  1334. "semicolons, or SQL comments."
  1335. )
  1336. with self.assertRaisesMessage(ValueError, msg):
  1337. Book.objects.alias(**{crafted_alias: Value(1)})