Browse Source

Fixed #28944 -- Fixed crash when chaining values()/values_list() after QuerySet.select_for_update(of=()).

Ran Benita 7 years ago
parent
commit
c21f158295
3 changed files with 34 additions and 8 deletions
  1. 9 8
      django/db/models/sql/compiler.py
  2. 3 0
      docs/releases/2.0.1.txt
  3. 22 0
      tests/select_for_update/tests.py

+ 9 - 8
django/db/models/sql/compiler.py

@@ -190,7 +190,7 @@ class SQLCompiler:
         "AS alias" for the column (possibly None).
 
         The klass_info structure contains the following information:
-        - Which model to instantiate
+        - The base model of the query.
         - Which columns for that model are present in the query (by
           position of the select clause).
         - related_klass_infos: [f, klass_info] to descent into
@@ -207,20 +207,21 @@ class SQLCompiler:
             select_idx += 1
         assert not (self.query.select and self.query.default_cols)
         if self.query.default_cols:
+            cols = self.get_default_columns()
+        else:
+            # self.query.select is a special case. These columns never go to
+            # any model.
+            cols = self.query.select
+        if cols:
             select_list = []
-            for c in self.get_default_columns():
+            for col in cols:
                 select_list.append(select_idx)
-                select.append((c, None))
+                select.append((col, None))
                 select_idx += 1
             klass_info = {
                 'model': self.query.model,
                 'select_fields': select_list,
             }
-        # self.query.select is a special case. These columns never go to
-        # any model.
-        for col in self.query.select:
-            select.append((col, None))
-            select_idx += 1
         for alias, annotation in self.query.annotation_select.items():
             annotations[alias] = select_idx
             select.append((annotation, alias))

+ 3 - 0
docs/releases/2.0.1.txt

@@ -37,3 +37,6 @@ Bugfixes
 
 * Fixed crash on SQLite when renaming a field in a model referenced by a
   ``ManyToManyField`` (:ticket:`28884`).
+
+* Fixed a crash when chaining ``values()`` or ``values_list()`` after
+  ``QuerySet.select_for_update(of=(...))`` (:ticket:`28944`).

+ 22 - 0
tests/select_for_update/tests.py

@@ -120,6 +120,28 @@ class SelectForUpdateTests(TransactionTestCase):
             expected = [value.upper() for value in expected]
         self.assertTrue(self.has_for_update_sql(ctx.captured_queries, of=expected))
 
+    @skipUnlessDBFeature('has_select_for_update_of')
+    def test_for_update_of_followed_by_values(self):
+        with transaction.atomic():
+            values = list(Person.objects.select_for_update(of=('self',)).values('pk'))
+        self.assertEqual(values, [{'pk': self.person.pk}])
+
+    @skipUnlessDBFeature('has_select_for_update_of')
+    def test_for_update_of_followed_by_values_list(self):
+        with transaction.atomic():
+            values = list(Person.objects.select_for_update(of=('self',)).values_list('pk'))
+        self.assertEqual(values, [(self.person.pk,)])
+
+    @skipUnlessDBFeature('has_select_for_update_of')
+    def test_for_update_of_self_when_self_is_not_selected(self):
+        """
+        select_for_update(of=['self']) when the only columns selected are from
+        related tables.
+        """
+        with transaction.atomic():
+            values = list(Person.objects.select_related('born').select_for_update(of=('self',)).values('born__name'))
+        self.assertEqual(values, [{'born__name': self.city1.name}])
+
     @skipUnlessDBFeature('has_select_for_update_nowait')
     def test_nowait_raises_error_on_block(self):
         """