diff --git a/django/db/models/query.py b/django/db/models/query.py index 8edae41e5f..b404fd1875 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1475,6 +1475,18 @@ class QuerySet(AltersData): "field." ) elif not fields: + # RemovedInDjango70Warning: When the deprecation ends, replace + # with: + # raise TypeError( + # "'flat' is not valid when values_list is called with no " + # "fields." + # ) + warnings.warn( + "Calling values_list() with no field name and flat=True " + "is deprecated. Pass an explicit field name instead, like " + "'pk'.", + RemovedInDjango70Warning, + ) fields = [self.model._meta.concrete_fields[0].attname] field_names = {f: False for f in fields if not hasattr(f, "resolve_expression")} diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index f2a6e9b545..2473d2b4f7 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -56,7 +56,8 @@ details on these changes. See the :ref:`Django 6.1 release notes ` for more details on these changes. -* ... +* Calling :meth:`.QuerySet.values_list` with ``flat=True`` and no field name + will raise ``TypeError``. .. _deprecation-removed-in-6.1: diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index 56a222f3e3..97a9d05d9d 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -262,7 +262,9 @@ Features deprecated in 6.1 Miscellaneous ------------- -* ... +* Calling :meth:`.QuerySet.values_list` with ``flat=True`` and no field name + is deprecated. Pass an explicit field name, like + ``values_list("pk", flat=True)``. Features removed in 6.1 ======================= diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py index f6f73e9fac..5b9dd8e5ec 100644 --- a/tests/lookup/tests.py +++ b/tests/lookup/tests.py @@ -30,7 +30,8 @@ from django.db.models.lookups import ( LessThanOrEqual, ) from django.test import TestCase, skipUnlessDBFeature -from django.test.utils import isolate_apps, register_lookup +from django.test.utils import ignore_warnings, isolate_apps, register_lookup +from django.utils.deprecation import RemovedInDjango70Warning from .models import ( Article, @@ -500,13 +501,19 @@ class LookupTests(TestCase): self.assertEqual(arts1.slug, "a1") self.assertEqual(arts1.headline, "Article 1") + # RemovedInDjango70Warning: When the deprecation ends, remove this + # test. def test_in_bulk_values_list_flat_empty(self): - arts = Article.objects.values_list(flat=True).in_bulk([]) + with ignore_warnings(category=RemovedInDjango70Warning): + arts = Article.objects.values_list(flat=True).in_bulk([]) self.assertEqual(arts, {}) + # RemovedInDjango70Warning: When the deprecation ends, remove this + # test. def test_in_bulk_values_list_flat_all(self): Article.objects.exclude(pk__in=[self.a1.pk, self.a2.pk]).delete() - arts = Article.objects.values_list(flat=True).in_bulk() + with ignore_warnings(category=RemovedInDjango70Warning): + arts = Article.objects.values_list(flat=True).in_bulk() self.assertEqual( arts, { @@ -515,8 +522,13 @@ class LookupTests(TestCase): }, ) + # RemovedInDjango70Warning: When the deprecation ends, remove this + # test. def test_in_bulk_values_list_flat_pks(self): - arts = Article.objects.values_list(flat=True).in_bulk([self.a1.pk, self.a2.pk]) + with ignore_warnings(category=RemovedInDjango70Warning): + arts = Article.objects.values_list(flat=True).in_bulk( + [self.a1.pk, self.a2.pk] + ) self.assertEqual( arts, { @@ -794,8 +806,12 @@ class LookupTests(TestCase): ), ], ) + # RemovedInDjango70Warning: When the deprecation ends, remove this + # assertion. + with ignore_warnings(category=RemovedInDjango70Warning): + qs = Article.objects.values_list(flat=True) self.assertSequenceEqual( - Article.objects.values_list(flat=True), + qs, [ self.a5.id, self.a6.id, @@ -902,6 +918,22 @@ class LookupTests(TestCase): with self.assertRaises(TypeError): Article.objects.values_list("id", "headline", flat=True) + # RemovedInDjango70Warning: When the deprecation ends, replace with: + # def test_values_list_flat_empty_error(self): + # msg = ( + # "'flat' is not valid when values_list is called with no fields." + # ) + # with self.assertRaisesMessage(TypeError, msg): + # Article.objects.values_list(flat=True) + def test_values_list_flat_empty_warning(self): + msg = ( + "Calling values_list() with no field name and flat=True " + "is deprecated. Pass an explicit field name instead, like " + "'pk'." + ) + with self.assertRaisesMessage(RemovedInDjango70Warning, msg): + Article.objects.values_list(flat=True) + def test_get_next_previous_by(self): # Every DateField and DateTimeField creates get_next_by_FOO() and # get_previous_by_FOO() methods. In the case of identical date values,