diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 7930d223f1..ddbcb0eb9a 100644 --- a/django/db/models/sql/compiler.py +++ b/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)) diff --git a/docs/releases/2.0.1.txt b/docs/releases/2.0.1.txt index ccbc7c29a3..97260ef5b8 100644 --- a/docs/releases/2.0.1.txt +++ b/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`). diff --git a/tests/select_for_update/tests.py b/tests/select_for_update/tests.py index 6de268cb2b..a750dc61db 100644 --- a/tests/select_for_update/tests.py +++ b/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): """