From 817bc5800b40bcc74534de5e5176919cb826494f Mon Sep 17 00:00:00 2001 From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:12:23 +0100 Subject: [PATCH] Refs #33651 -- Removed Prefetch.get_current_queryset() and get_prefetch_queryset() per deprecation timeline. --- django/contrib/contenttypes/fields.py | 24 ------- .../db/models/fields/related_descriptors.py | 47 -------------- django/db/models/query.py | 63 ++++--------------- docs/releases/6.0.txt | 8 +++ tests/contenttypes_tests/test_fields.py | 24 ------- tests/many_to_many/tests.py | 11 ---- tests/many_to_one/tests.py | 24 ------- tests/one_to_one/tests.py | 11 ---- tests/prefetch_related/tests.py | 61 +----------------- 9 files changed, 21 insertions(+), 252 deletions(-) diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py index a3e87f6ed4..0e68c2eef0 100644 --- a/django/contrib/contenttypes/fields.py +++ b/django/contrib/contenttypes/fields.py @@ -1,6 +1,5 @@ import functools import itertools -import warnings from collections import defaultdict from asgiref.sync import sync_to_async @@ -21,7 +20,6 @@ from django.db.models.query_utils import PathInfo from django.db.models.sql import AND from django.db.models.sql.where import WhereNode from django.db.models.utils import AltersData -from django.utils.deprecation import RemovedInDjango60Warning from django.utils.functional import cached_property @@ -159,17 +157,6 @@ class GenericForeignKey(FieldCacheMixin, Field): # This should never happen. I love comments like this, don't you? raise Exception("Impossible arguments to GFK.get_content_type!") - def get_prefetch_queryset(self, instances, queryset=None): - warnings.warn( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead.", - RemovedInDjango60Warning, - stacklevel=2, - ) - if queryset is None: - return self.get_prefetch_querysets(instances) - return self.get_prefetch_querysets(instances, [queryset]) - def get_prefetch_querysets(self, instances, querysets=None): custom_queryset_dict = {} if querysets is not None: @@ -626,17 +613,6 @@ def create_generic_related_manager(superclass, rel): queryset = super().get_queryset() return self._apply_rel_filters(queryset) - def get_prefetch_queryset(self, instances, queryset=None): - warnings.warn( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead.", - RemovedInDjango60Warning, - stacklevel=2, - ) - if queryset is None: - return self.get_prefetch_querysets(instances) - return self.get_prefetch_querysets(instances, [queryset]) - def get_prefetch_querysets(self, instances, querysets=None): if querysets and len(querysets) != 1: raise ValueError( diff --git a/django/db/models/fields/related_descriptors.py b/django/db/models/fields/related_descriptors.py index 66c978d42e..bbbcc07990 100644 --- a/django/db/models/fields/related_descriptors.py +++ b/django/db/models/fields/related_descriptors.py @@ -63,8 +63,6 @@ and two directions (forward and reverse) for a total of six combinations. ``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead. """ -import warnings - from asgiref.sync import sync_to_async from django.core.exceptions import FieldError @@ -81,7 +79,6 @@ from django.db.models.lookups import GreaterThan, LessThanOrEqual from django.db.models.query import QuerySet from django.db.models.query_utils import DeferredAttribute from django.db.models.utils import AltersData, resolve_callables -from django.utils.deprecation import RemovedInDjango60Warning from django.utils.functional import cached_property @@ -155,17 +152,6 @@ class ForwardManyToOneDescriptor: def get_queryset(self, **hints): return self.field.remote_field.model._base_manager.db_manager(hints=hints).all() - def get_prefetch_queryset(self, instances, queryset=None): - warnings.warn( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead.", - RemovedInDjango60Warning, - stacklevel=2, - ) - if queryset is None: - return self.get_prefetch_querysets(instances) - return self.get_prefetch_querysets(instances, [queryset]) - def get_prefetch_querysets(self, instances, querysets=None): if querysets and len(querysets) != 1: raise ValueError( @@ -447,17 +433,6 @@ class ReverseOneToOneDescriptor: def get_queryset(self, **hints): return self.related.related_model._base_manager.db_manager(hints=hints).all() - def get_prefetch_queryset(self, instances, queryset=None): - warnings.warn( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead.", - RemovedInDjango60Warning, - stacklevel=2, - ) - if queryset is None: - return self.get_prefetch_querysets(instances) - return self.get_prefetch_querysets(instances, [queryset]) - def get_prefetch_querysets(self, instances, querysets=None): if querysets and len(querysets) != 1: raise ValueError( @@ -765,17 +740,6 @@ def create_reverse_many_to_one_manager(superclass, rel): queryset = super().get_queryset() return self._apply_rel_filters(queryset) - def get_prefetch_queryset(self, instances, queryset=None): - warnings.warn( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead.", - RemovedInDjango60Warning, - stacklevel=2, - ) - if queryset is None: - return self.get_prefetch_querysets(instances) - return self.get_prefetch_querysets(instances, [queryset]) - def get_prefetch_querysets(self, instances, querysets=None): if querysets and len(querysets) != 1: raise ValueError( @@ -1145,17 +1109,6 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): queryset = super().get_queryset() return self._apply_rel_filters(queryset) - def get_prefetch_queryset(self, instances, queryset=None): - warnings.warn( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead.", - RemovedInDjango60Warning, - stacklevel=2, - ) - if queryset is None: - return self.get_prefetch_querysets(instances) - return self.get_prefetch_querysets(instances, [queryset]) - def get_prefetch_querysets(self, instances, querysets=None): if querysets and len(querysets) != 1: raise ValueError( diff --git a/django/db/models/query.py b/django/db/models/query.py index 25995b0d83..eb17624bf1 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -33,7 +33,6 @@ from django.db.models.utils import ( resolve_callables, ) from django.utils import timezone -from django.utils.deprecation import RemovedInDjango60Warning from django.utils.functional import cached_property, partition # The maximum number of results to fetch in a get() query. @@ -2239,16 +2238,6 @@ class Prefetch: as_attr = self.to_attr and level == len(parts) - 1 return to_attr, as_attr - def get_current_queryset(self, level): - warnings.warn( - "Prefetch.get_current_queryset() is deprecated. Use " - "get_current_querysets() instead.", - RemovedInDjango60Warning, - stacklevel=2, - ) - querysets = self.get_current_querysets(level) - return querysets[0] if querysets is not None else None - def get_current_querysets(self, level): if ( self.get_current_prefetch_to(level) == self.prefetch_to @@ -2480,11 +2469,7 @@ def get_prefetcher(instance, through_attr, to_attr): if rel_obj_descriptor: # singly related object, descriptor object has the # get_prefetch_querysets() method. - if ( - hasattr(rel_obj_descriptor, "get_prefetch_querysets") - # RemovedInDjango60Warning. - or hasattr(rel_obj_descriptor, "get_prefetch_queryset") - ): + if hasattr(rel_obj_descriptor, "get_prefetch_querysets"): prefetcher = rel_obj_descriptor # If to_attr is set, check if the value has already been set, # which is done with has_to_attr_attribute(). Do not use the @@ -2497,11 +2482,7 @@ def get_prefetcher(instance, through_attr, to_attr): # the attribute on the instance rather than the class to # support many related managers rel_obj = getattr(instance, through_attr) - if ( - hasattr(rel_obj, "get_prefetch_querysets") - # RemovedInDjango60Warning. - or hasattr(rel_obj, "get_prefetch_queryset") - ): + if hasattr(rel_obj, "get_prefetch_querysets"): prefetcher = rel_obj if through_attr == to_attr: @@ -2534,36 +2515,16 @@ def prefetch_one_level(instances, prefetcher, lookup, level): # The 'values to be matched' must be hashable as they will be used # in a dictionary. - - if hasattr(prefetcher, "get_prefetch_querysets"): - ( - rel_qs, - rel_obj_attr, - instance_attr, - single, - cache_name, - is_descriptor, - ) = prefetcher.get_prefetch_querysets( - instances, lookup.get_current_querysets(level) - ) - else: - warnings.warn( - "The usage of get_prefetch_queryset() in prefetch_related_objects() is " - "deprecated. Implement get_prefetch_querysets() instead.", - RemovedInDjango60Warning, - stacklevel=2, - ) - queryset = None - if querysets := lookup.get_current_querysets(level): - queryset = querysets[0] - ( - rel_qs, - rel_obj_attr, - instance_attr, - single, - cache_name, - is_descriptor, - ) = prefetcher.get_prefetch_queryset(instances, queryset) + ( + rel_qs, + rel_obj_attr, + instance_attr, + single, + cache_name, + is_descriptor, + ) = prefetcher.get_prefetch_querysets( + instances, lookup.get_current_querysets(level) + ) # We have to handle the possibility that the QuerySet we just got back # contains some prefetch_related lookups. We don't want to trigger the # prefetch_related functionality by evaluating the query. Rather, we need diff --git a/docs/releases/6.0.txt b/docs/releases/6.0.txt index 596b11b27c..1f6f9a79da 100644 --- a/docs/releases/6.0.txt +++ b/docs/releases/6.0.txt @@ -286,6 +286,14 @@ to remove usage of these features. * The ``ChoicesMeta`` alias to ``django.db.models.enums.ChoicesType`` is removed. +* The ``Prefetch.get_current_queryset()`` method is removed. + +* The ``get_prefetch_queryset()`` method of related managers and descriptors is + removed. + +* ``get_prefetcher()`` and ``prefetch_related_objects()`` no longer fallback to + ``get_prefetch_queryset()``. + See :ref:`deprecated-features-5.1` for details on these changes, including how to remove usage of these features. diff --git a/tests/contenttypes_tests/test_fields.py b/tests/contenttypes_tests/test_fields.py index fc49d59b27..764b9fa7db 100644 --- a/tests/contenttypes_tests/test_fields.py +++ b/tests/contenttypes_tests/test_fields.py @@ -5,7 +5,6 @@ from django.contrib.contenttypes.prefetch import GenericPrefetch from django.db import models from django.test import TestCase from django.test.utils import isolate_apps -from django.utils.deprecation import RemovedInDjango60Warning from .models import Answer, Post, Question @@ -99,29 +98,6 @@ class DeferredGenericRelationTests(TestCase): obj.question -class GetPrefetchQuerySetDeprecation(TestCase): - def test_generic_relation_warning(self): - Question.objects.create(text="test") - questions = Question.objects.all() - msg = ( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead." - ) - with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx: - questions[0].answer_set.get_prefetch_queryset(questions) - self.assertEqual(ctx.filename, __file__) - - def test_generic_foreign_key_warning(self): - answers = Answer.objects.all() - msg = ( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead." - ) - with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx: - Answer.question.get_prefetch_queryset(answers) - self.assertEqual(ctx.filename, __file__) - - class GetPrefetchQuerySetsTests(TestCase): def test_duplicate_querysets(self): question = Question.objects.create(text="What is your name?") diff --git a/tests/many_to_many/tests.py b/tests/many_to_many/tests.py index c17ed0258c..1535ef4105 100644 --- a/tests/many_to_many/tests.py +++ b/tests/many_to_many/tests.py @@ -2,7 +2,6 @@ from unittest import mock from django.db import connection, transaction from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature -from django.utils.deprecation import RemovedInDjango60Warning from .models import ( Article, @@ -577,16 +576,6 @@ class ManyToManyTests(TestCase): ) self.assertIn("JOIN", ctx.captured_queries[0]["sql"]) - def test_get_prefetch_queryset_warning(self): - articles = Article.objects.all() - msg = ( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead." - ) - with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx: - self.a1.publications.get_prefetch_queryset(articles) - self.assertEqual(ctx.filename, __file__) - def test_get_prefetch_querysets_invalid_querysets_length(self): articles = Article.objects.all() msg = ( diff --git a/tests/many_to_one/tests.py b/tests/many_to_one/tests.py index e7dd0f229f..5e31ea1760 100644 --- a/tests/many_to_one/tests.py +++ b/tests/many_to_one/tests.py @@ -4,7 +4,6 @@ from copy import deepcopy from django.core.exceptions import FieldError, MultipleObjectsReturned from django.db import IntegrityError, models, transaction from django.test import TestCase -from django.utils.deprecation import RemovedInDjango60Warning from django.utils.translation import gettext_lazy from .models import ( @@ -887,29 +886,6 @@ class ManyToOneTests(TestCase): with self.assertRaisesMessage(TypeError, msg): usa.cities.set([chicago.pk]) - def test_get_prefetch_queryset_warning(self): - City.objects.create(name="Chicago") - cities = City.objects.all() - msg = ( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead." - ) - with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx: - City.country.get_prefetch_queryset(cities) - self.assertEqual(ctx.filename, __file__) - - def test_get_prefetch_queryset_reverse_warning(self): - usa = Country.objects.create(name="United States") - City.objects.create(name="Chicago") - countries = Country.objects.all() - msg = ( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead." - ) - with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx: - usa.cities.get_prefetch_queryset(countries) - self.assertEqual(ctx.filename, __file__) - def test_get_prefetch_querysets_invalid_querysets_length(self): City.objects.create(name="Chicago") cities = City.objects.all() diff --git a/tests/one_to_one/tests.py b/tests/one_to_one/tests.py index 0d30dd8f27..451e97c274 100644 --- a/tests/one_to_one/tests.py +++ b/tests/one_to_one/tests.py @@ -1,6 +1,5 @@ from django.db import IntegrityError, connection, transaction from django.test import TestCase -from django.utils.deprecation import RemovedInDjango60Warning from .models import ( Bar, @@ -606,16 +605,6 @@ class OneToOneTests(TestCase): self.b1.save() self.assertEqual(self.b1.place, self.p2) - def test_get_prefetch_queryset_warning(self): - places = Place.objects.all() - msg = ( - "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() " - "instead." - ) - with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx: - Place.bar.get_prefetch_queryset(places) - self.assertEqual(ctx.filename, __file__) - def test_get_prefetch_querysets_invalid_querysets_length(self): places = Place.objects.all() msg = ( diff --git a/tests/prefetch_related/tests.py b/tests/prefetch_related/tests.py index 856f766d30..b44a34c456 100644 --- a/tests/prefetch_related/tests.py +++ b/tests/prefetch_related/tests.py @@ -4,18 +4,15 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist from django.db import NotSupportedError, connection from django.db.models import Prefetch, QuerySet, prefetch_related_objects -from django.db.models.fields.related import ForwardManyToOneDescriptor -from django.db.models.query import get_prefetcher, prefetch_one_level +from django.db.models.query import get_prefetcher from django.db.models.sql import Query from django.test import ( TestCase, - ignore_warnings, override_settings, skipIfDBFeature, skipUnlessDBFeature, ) from django.test.utils import CaptureQueriesContext -from django.utils.deprecation import RemovedInDjango60Warning from .models import ( Article, @@ -2013,59 +2010,3 @@ class PrefetchLimitTests(TestDataMixin, TestCase): with self.subTest(book=book): self.assertEqual(len(book.authors_sliced), 1) self.assertIn(book.authors_sliced[0], list(book.authors.all())) - - -class DeprecationTests(TestCase): - def test_get_current_queryset_warning(self): - msg = ( - "Prefetch.get_current_queryset() is deprecated. Use " - "get_current_querysets() instead." - ) - authors = Author.objects.all() - with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx: - self.assertEqual( - Prefetch("authors", authors).get_current_queryset(1), - authors, - ) - self.assertEqual(ctx.filename, __file__) - with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx: - self.assertIsNone(Prefetch("authors").get_current_queryset(1)) - self.assertEqual(ctx.filename, __file__) - - @ignore_warnings(category=RemovedInDjango60Warning) - def test_prefetch_one_level_fallback(self): - class NoGetPrefetchQuerySetsDescriptor(ForwardManyToOneDescriptor): - def get_prefetch_queryset(self, instances, queryset=None): - if queryset is None: - return super().get_prefetch_querysets(instances) - return super().get_prefetch_querysets(instances, [queryset]) - - def __getattribute__(self, name): - if name == "get_prefetch_querysets": - raise AttributeError - return super().__getattribute__(name) - - house = House.objects.create() - room = Room.objects.create(house=house) - house.main_room = room - house.save() - - # prefetch_one_level() fallbacks to get_prefetch_queryset(). - prefetcher = NoGetPrefetchQuerySetsDescriptor(Room._meta.get_field("house")) - obj_list, additional_lookups = prefetch_one_level( - [room], - prefetcher, - Prefetch("house", House.objects.all()), - 0, - ) - self.assertEqual(obj_list, [house]) - self.assertEqual(additional_lookups, []) - - obj_list, additional_lookups = prefetch_one_level( - [room], - prefetcher, - Prefetch("house"), - 0, - ) - self.assertEqual(obj_list, [house]) - self.assertEqual(additional_lookups, [])