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

This commit is contained in:
Ran Benita 2017-12-23 11:35:08 +02:00 committed by Tim Graham
parent 5778b5701d
commit c21f158295
3 changed files with 34 additions and 8 deletions

View File

@ -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))

View File

@ -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`).

View File

@ -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):
"""