1. import datetime
    
  2. 
    
  3. from django.contrib.auth.models import User
    
  4. from django.test import TestCase
    
  5. 
    
  6. from .models import Order, RevisionableModel, TestObject
    
  7. 
    
  8. 
    
  9. class ExtraRegressTests(TestCase):
    
  10.     @classmethod
    
  11.     def setUpTestData(cls):
    
  12.         cls.u = User.objects.create_user(
    
  13.             username="fred", password="secret", email="[email protected]"
    
  14.         )
    
  15. 
    
  16.     def test_regression_7314_7372(self):
    
  17.         """
    
  18.         Regression tests for #7314 and #7372
    
  19.         """
    
  20.         rm = RevisionableModel.objects.create(
    
  21.             title="First Revision", when=datetime.datetime(2008, 9, 28, 10, 30, 0)
    
  22.         )
    
  23.         self.assertEqual(rm.pk, rm.base.pk)
    
  24. 
    
  25.         rm2 = rm.new_revision()
    
  26.         rm2.title = "Second Revision"
    
  27.         rm.when = datetime.datetime(2008, 9, 28, 14, 25, 0)
    
  28.         rm2.save()
    
  29. 
    
  30.         self.assertEqual(rm2.title, "Second Revision")
    
  31.         self.assertEqual(rm2.base.title, "First Revision")
    
  32. 
    
  33.         self.assertNotEqual(rm2.pk, rm.pk)
    
  34.         self.assertEqual(rm2.base.pk, rm.pk)
    
  35. 
    
  36.         # Queryset to match most recent revision:
    
  37.         qs = RevisionableModel.objects.extra(
    
  38.             where=[
    
  39.                 "%(table)s.id IN "
    
  40.                 "(SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)"
    
  41.                 % {
    
  42.                     "table": RevisionableModel._meta.db_table,
    
  43.                 }
    
  44.             ]
    
  45.         )
    
  46. 
    
  47.         self.assertQuerysetEqual(
    
  48.             qs,
    
  49.             [("Second Revision", "First Revision")],
    
  50.             transform=lambda r: (r.title, r.base.title),
    
  51.         )
    
  52. 
    
  53.         # Queryset to search for string in title:
    
  54.         qs2 = RevisionableModel.objects.filter(title__contains="Revision")
    
  55.         self.assertQuerysetEqual(
    
  56.             qs2,
    
  57.             [
    
  58.                 ("First Revision", "First Revision"),
    
  59.                 ("Second Revision", "First Revision"),
    
  60.             ],
    
  61.             transform=lambda r: (r.title, r.base.title),
    
  62.             ordered=False,
    
  63.         )
    
  64. 
    
  65.         # Following queryset should return the most recent revision:
    
  66.         self.assertQuerysetEqual(
    
  67.             qs & qs2,
    
  68.             [("Second Revision", "First Revision")],
    
  69.             transform=lambda r: (r.title, r.base.title),
    
  70.             ordered=False,
    
  71.         )
    
  72. 
    
  73.     def test_extra_stay_tied(self):
    
  74.         # Extra select parameters should stay tied to their corresponding
    
  75.         # select portions. Applies when portions are updated or otherwise
    
  76.         # moved around.
    
  77.         qs = User.objects.extra(
    
  78.             select={"alpha": "%s", "beta": "2", "gamma": "%s"}, select_params=(1, 3)
    
  79.         )
    
  80.         qs = qs.extra(select={"beta": 4})
    
  81.         qs = qs.extra(select={"alpha": "%s"}, select_params=[5])
    
  82.         self.assertEqual(
    
  83.             list(qs.filter(id=self.u.id).values("alpha", "beta", "gamma")),
    
  84.             [{"alpha": 5, "beta": 4, "gamma": 3}],
    
  85.         )
    
  86. 
    
  87.     def test_regression_7957(self):
    
  88.         """
    
  89.         Regression test for #7957: Combining extra() calls should leave the
    
  90.         corresponding parameters associated with the right extra() bit. I.e.
    
  91.         internal dictionary must remain sorted.
    
  92.         """
    
  93.         self.assertEqual(
    
  94.             (
    
  95.                 User.objects.extra(select={"alpha": "%s"}, select_params=(1,))
    
  96.                 .extra(select={"beta": "%s"}, select_params=(2,))[0]
    
  97.                 .alpha
    
  98.             ),
    
  99.             1,
    
  100.         )
    
  101. 
    
  102.         self.assertEqual(
    
  103.             (
    
  104.                 User.objects.extra(select={"beta": "%s"}, select_params=(1,))
    
  105.                 .extra(select={"alpha": "%s"}, select_params=(2,))[0]
    
  106.                 .alpha
    
  107.             ),
    
  108.             2,
    
  109.         )
    
  110. 
    
  111.     def test_regression_7961(self):
    
  112.         """
    
  113.         Regression test for #7961: When not using a portion of an
    
  114.         extra(...) in a query, remove any corresponding parameters from the
    
  115.         query as well.
    
  116.         """
    
  117.         self.assertEqual(
    
  118.             list(
    
  119.                 User.objects.extra(select={"alpha": "%s"}, select_params=(-6,))
    
  120.                 .filter(id=self.u.id)
    
  121.                 .values_list("id", flat=True)
    
  122.             ),
    
  123.             [self.u.id],
    
  124.         )
    
  125. 
    
  126.     def test_regression_8063(self):
    
  127.         """
    
  128.         Regression test for #8063: limiting a query shouldn't discard any
    
  129.         extra() bits.
    
  130.         """
    
  131.         qs = User.objects.extra(where=["id=%s"], params=[self.u.id])
    
  132.         self.assertSequenceEqual(qs, [self.u])
    
  133.         self.assertSequenceEqual(qs[:1], [self.u])
    
  134. 
    
  135.     def test_regression_8039(self):
    
  136.         """
    
  137.         Regression test for #8039: Ordering sometimes removed relevant tables
    
  138.         from extra(). This test is the critical case: ordering uses a table,
    
  139.         but then removes the reference because of an optimization. The table
    
  140.         should still be present because of the extra() call.
    
  141.         """
    
  142.         self.assertQuerysetEqual(
    
  143.             (
    
  144.                 Order.objects.extra(
    
  145.                     where=["username=%s"], params=["fred"], tables=["auth_user"]
    
  146.                 ).order_by("created_by")
    
  147.             ),
    
  148.             [],
    
  149.         )
    
  150. 
    
  151.     def test_regression_8819(self):
    
  152.         """
    
  153.         Regression test for #8819: Fields in the extra(select=...) list
    
  154.         should be available to extra(order_by=...).
    
  155.         """
    
  156.         self.assertSequenceEqual(
    
  157.             User.objects.filter(pk=self.u.id)
    
  158.             .extra(select={"extra_field": 1})
    
  159.             .distinct(),
    
  160.             [self.u],
    
  161.         )
    
  162.         self.assertSequenceEqual(
    
  163.             User.objects.filter(pk=self.u.id).extra(
    
  164.                 select={"extra_field": 1}, order_by=["extra_field"]
    
  165.             ),
    
  166.             [self.u],
    
  167.         )
    
  168.         self.assertSequenceEqual(
    
  169.             User.objects.filter(pk=self.u.id)
    
  170.             .extra(select={"extra_field": 1}, order_by=["extra_field"])
    
  171.             .distinct(),
    
  172.             [self.u],
    
  173.         )
    
  174. 
    
  175.     def test_dates_query(self):
    
  176.         """
    
  177.         When calling the dates() method on a queryset with extra selection
    
  178.         columns, we can (and should) ignore those columns. They don't change
    
  179.         the result and cause incorrect SQL to be produced otherwise.
    
  180.         """
    
  181.         RevisionableModel.objects.create(
    
  182.             title="First Revision", when=datetime.datetime(2008, 9, 28, 10, 30, 0)
    
  183.         )
    
  184. 
    
  185.         self.assertSequenceEqual(
    
  186.             RevisionableModel.objects.extra(select={"the_answer": "id"}).datetimes(
    
  187.                 "when", "month"
    
  188.             ),
    
  189.             [datetime.datetime(2008, 9, 1, 0, 0)],
    
  190.         )
    
  191. 
    
  192.     def test_values_with_extra(self):
    
  193.         """
    
  194.         Regression test for #10256... If there is a values() clause, Extra
    
  195.         columns are only returned if they are explicitly mentioned.
    
  196.         """
    
  197.         obj = TestObject(first="first", second="second", third="third")
    
  198.         obj.save()
    
  199. 
    
  200.         self.assertEqual(
    
  201.             list(
    
  202.                 TestObject.objects.extra(
    
  203.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  204.                 ).values()
    
  205.             ),
    
  206.             [
    
  207.                 {
    
  208.                     "bar": "second",
    
  209.                     "third": "third",
    
  210.                     "second": "second",
    
  211.                     "whiz": "third",
    
  212.                     "foo": "first",
    
  213.                     "id": obj.pk,
    
  214.                     "first": "first",
    
  215.                 }
    
  216.             ],
    
  217.         )
    
  218. 
    
  219.         # Extra clauses after an empty values clause are still included
    
  220.         self.assertEqual(
    
  221.             list(
    
  222.                 TestObject.objects.values().extra(
    
  223.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  224.                 )
    
  225.             ),
    
  226.             [
    
  227.                 {
    
  228.                     "bar": "second",
    
  229.                     "third": "third",
    
  230.                     "second": "second",
    
  231.                     "whiz": "third",
    
  232.                     "foo": "first",
    
  233.                     "id": obj.pk,
    
  234.                     "first": "first",
    
  235.                 }
    
  236.             ],
    
  237.         )
    
  238. 
    
  239.         # Extra columns are ignored if not mentioned in the values() clause
    
  240.         self.assertEqual(
    
  241.             list(
    
  242.                 TestObject.objects.extra(
    
  243.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  244.                 ).values("first", "second")
    
  245.             ),
    
  246.             [{"second": "second", "first": "first"}],
    
  247.         )
    
  248. 
    
  249.         # Extra columns after a non-empty values() clause are ignored
    
  250.         self.assertEqual(
    
  251.             list(
    
  252.                 TestObject.objects.values("first", "second").extra(
    
  253.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  254.                 )
    
  255.             ),
    
  256.             [{"second": "second", "first": "first"}],
    
  257.         )
    
  258. 
    
  259.         # Extra columns can be partially returned
    
  260.         self.assertEqual(
    
  261.             list(
    
  262.                 TestObject.objects.extra(
    
  263.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  264.                 ).values("first", "second", "foo")
    
  265.             ),
    
  266.             [{"second": "second", "foo": "first", "first": "first"}],
    
  267.         )
    
  268. 
    
  269.         # Also works if only extra columns are included
    
  270.         self.assertEqual(
    
  271.             list(
    
  272.                 TestObject.objects.extra(
    
  273.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  274.                 ).values("foo", "whiz")
    
  275.             ),
    
  276.             [{"foo": "first", "whiz": "third"}],
    
  277.         )
    
  278. 
    
  279.         # Values list works the same way
    
  280.         # All columns are returned for an empty values_list()
    
  281.         self.assertEqual(
    
  282.             list(
    
  283.                 TestObject.objects.extra(
    
  284.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  285.                 ).values_list()
    
  286.             ),
    
  287.             [("first", "second", "third", obj.pk, "first", "second", "third")],
    
  288.         )
    
  289. 
    
  290.         # Extra columns after an empty values_list() are still included
    
  291.         self.assertEqual(
    
  292.             list(
    
  293.                 TestObject.objects.values_list().extra(
    
  294.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  295.                 )
    
  296.             ),
    
  297.             [("first", "second", "third", obj.pk, "first", "second", "third")],
    
  298.         )
    
  299. 
    
  300.         # Extra columns ignored completely if not mentioned in values_list()
    
  301.         self.assertEqual(
    
  302.             list(
    
  303.                 TestObject.objects.extra(
    
  304.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  305.                 ).values_list("first", "second")
    
  306.             ),
    
  307.             [("first", "second")],
    
  308.         )
    
  309. 
    
  310.         # Extra columns after a non-empty values_list() clause are ignored completely
    
  311.         self.assertEqual(
    
  312.             list(
    
  313.                 TestObject.objects.values_list("first", "second").extra(
    
  314.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  315.                 )
    
  316.             ),
    
  317.             [("first", "second")],
    
  318.         )
    
  319. 
    
  320.         self.assertEqual(
    
  321.             list(
    
  322.                 TestObject.objects.extra(
    
  323.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  324.                 ).values_list("second", flat=True)
    
  325.             ),
    
  326.             ["second"],
    
  327.         )
    
  328. 
    
  329.         # Only the extra columns specified in the values_list() are returned
    
  330.         self.assertEqual(
    
  331.             list(
    
  332.                 TestObject.objects.extra(
    
  333.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  334.                 ).values_list("first", "second", "whiz")
    
  335.             ),
    
  336.             [("first", "second", "third")],
    
  337.         )
    
  338. 
    
  339.         # ...also works if only extra columns are included
    
  340.         self.assertEqual(
    
  341.             list(
    
  342.                 TestObject.objects.extra(
    
  343.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  344.                 ).values_list("foo", "whiz")
    
  345.             ),
    
  346.             [("first", "third")],
    
  347.         )
    
  348. 
    
  349.         self.assertEqual(
    
  350.             list(
    
  351.                 TestObject.objects.extra(
    
  352.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  353.                 ).values_list("whiz", flat=True)
    
  354.             ),
    
  355.             ["third"],
    
  356.         )
    
  357. 
    
  358.         # ... and values are returned in the order they are specified
    
  359.         self.assertEqual(
    
  360.             list(
    
  361.                 TestObject.objects.extra(
    
  362.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  363.                 ).values_list("whiz", "foo")
    
  364.             ),
    
  365.             [("third", "first")],
    
  366.         )
    
  367. 
    
  368.         self.assertEqual(
    
  369.             list(
    
  370.                 TestObject.objects.extra(
    
  371.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  372.                 ).values_list("first", "id")
    
  373.             ),
    
  374.             [("first", obj.pk)],
    
  375.         )
    
  376. 
    
  377.         self.assertEqual(
    
  378.             list(
    
  379.                 TestObject.objects.extra(
    
  380.                     select={"foo": "first", "bar": "second", "whiz": "third"}
    
  381.                 ).values_list("whiz", "first", "bar", "id")
    
  382.             ),
    
  383.             [("third", "first", "second", obj.pk)],
    
  384.         )
    
  385. 
    
  386.     def test_regression_10847(self):
    
  387.         """
    
  388.         Regression for #10847: the list of extra columns can always be
    
  389.         accurately evaluated. Using an inner query ensures that as_sql() is
    
  390.         producing correct output without requiring full evaluation and
    
  391.         execution of the inner query.
    
  392.         """
    
  393.         obj = TestObject(first="first", second="second", third="third")
    
  394.         obj.save()
    
  395. 
    
  396.         self.assertEqual(
    
  397.             list(TestObject.objects.extra(select={"extra": 1}).values("pk")),
    
  398.             [{"pk": obj.pk}],
    
  399.         )
    
  400. 
    
  401.         self.assertSequenceEqual(
    
  402.             TestObject.objects.filter(
    
  403.                 pk__in=TestObject.objects.extra(select={"extra": 1}).values("pk")
    
  404.             ),
    
  405.             [obj],
    
  406.         )
    
  407. 
    
  408.         self.assertEqual(
    
  409.             list(TestObject.objects.values("pk").extra(select={"extra": 1})),
    
  410.             [{"pk": obj.pk}],
    
  411.         )
    
  412. 
    
  413.         self.assertSequenceEqual(
    
  414.             TestObject.objects.filter(
    
  415.                 pk__in=TestObject.objects.values("pk").extra(select={"extra": 1})
    
  416.             ),
    
  417.             [obj],
    
  418.         )
    
  419. 
    
  420.         self.assertSequenceEqual(
    
  421.             TestObject.objects.filter(pk=obj.pk)
    
  422.             | TestObject.objects.extra(where=["id > %s"], params=[obj.pk]),
    
  423.             [obj],
    
  424.         )
    
  425. 
    
  426.     def test_regression_17877(self):
    
  427.         """
    
  428.         Extra WHERE clauses get correctly ANDed, even when they
    
  429.         contain OR operations.
    
  430.         """
    
  431.         # Test Case 1: should appear in queryset.
    
  432.         t1 = TestObject.objects.create(first="a", second="a", third="a")
    
  433.         # Test Case 2: should appear in queryset.
    
  434.         t2 = TestObject.objects.create(first="b", second="a", third="a")
    
  435.         # Test Case 3: should not appear in queryset, bug case.
    
  436.         t = TestObject(first="a", second="a", third="b")
    
  437.         t.save()
    
  438.         # Test Case 4: should not appear in queryset.
    
  439.         t = TestObject(first="b", second="a", third="b")
    
  440.         t.save()
    
  441.         # Test Case 5: should not appear in queryset.
    
  442.         t = TestObject(first="b", second="b", third="a")
    
  443.         t.save()
    
  444.         # Test Case 6: should not appear in queryset, bug case.
    
  445.         t = TestObject(first="a", second="b", third="b")
    
  446.         t.save()
    
  447. 
    
  448.         self.assertCountEqual(
    
  449.             TestObject.objects.extra(
    
  450.                 where=["first = 'a' OR second = 'a'", "third = 'a'"],
    
  451.             ),
    
  452.             [t1, t2],
    
  453.         )
    
  454. 
    
  455.     def test_extra_values_distinct_ordering(self):
    
  456.         t1 = TestObject.objects.create(first="a", second="a", third="a")
    
  457.         t2 = TestObject.objects.create(first="a", second="b", third="b")
    
  458.         qs = (
    
  459.             TestObject.objects.extra(select={"second_extra": "second"})
    
  460.             .values_list("id", flat=True)
    
  461.             .distinct()
    
  462.         )
    
  463.         self.assertSequenceEqual(qs.order_by("second_extra"), [t1.pk, t2.pk])
    
  464.         self.assertSequenceEqual(qs.order_by("-second_extra"), [t2.pk, t1.pk])
    
  465.         # Note: the extra ordering must appear in select clause, so we get two
    
  466.         # non-distinct results here (this is on purpose, see #7070).
    
  467.         # Extra select doesn't appear in result values.
    
  468.         self.assertSequenceEqual(
    
  469.             qs.order_by("-second_extra").values_list("first"), [("a",), ("a",)]
    
  470.         )