From 4d78d7338cc5cfaa0afcfc9d240d753328bbf399 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Thu, 12 Jan 2023 19:31:49 +0100 Subject: [PATCH] Refs #31486 -- Removed ability to pass unsaved model instances to related filters. Per deprecation timeline. --- django/db/models/fields/related_lookups.py | 12 +------- docs/releases/5.0.txt | 2 ++ tests/queries/tests.py | 36 ++++++---------------- 3 files changed, 12 insertions(+), 38 deletions(-) diff --git a/django/db/models/fields/related_lookups.py b/django/db/models/fields/related_lookups.py index afea09b5a9a..07a06e16862 100644 --- a/django/db/models/fields/related_lookups.py +++ b/django/db/models/fields/related_lookups.py @@ -1,5 +1,3 @@ -import warnings - from django.db.models.lookups import ( Exact, GreaterThan, @@ -9,7 +7,6 @@ from django.db.models.lookups import ( LessThan, LessThanOrEqual, ) -from django.utils.deprecation import RemovedInDjango50Warning class MultiColSource: @@ -45,14 +42,7 @@ def get_normalized_value(value, lhs): if isinstance(value, Model): if value.pk is None: - # When the deprecation ends, replace with: - # raise ValueError( - # "Model instances passed to related filters must be saved." - # ) - warnings.warn( - "Passing unsaved model instances to related filters is deprecated.", - RemovedInDjango50Warning, - ) + raise ValueError("Model instances passed to related filters must be saved.") value_list = [] sources = lhs.output_field.path_infos[-1].target_fields for source in sources: diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt index e058f4a76c7..a5c59165116 100644 --- a/docs/releases/5.0.txt +++ b/docs/releases/5.0.txt @@ -322,3 +322,5 @@ to remove usage of these features. * The usage of ``QuerySet.iterator()`` on a queryset that prefetches related objects without providing the ``chunk_size`` argument is no longer allowed. + +* Passing unsaved model instances to related filters is no longer allowed. diff --git a/tests/queries/tests.py b/tests/queries/tests.py index a6a2b252eb8..9578777b110 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -13,8 +13,7 @@ from django.db.models.functions import ExtractYear, Length, LTrim from django.db.models.sql.constants import LOUTER from django.db.models.sql.where import AND, OR, NothingNode, WhereNode from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature -from django.test.utils import CaptureQueriesContext, ignore_warnings, register_lookup -from django.utils.deprecation import RemovedInDjango50Warning +from django.test.utils import CaptureQueriesContext, register_lookup from .models import ( FK1, @@ -1956,16 +1955,13 @@ class Queries5Tests(TestCase): self.assertEqual(authors.count(), 1) def test_filter_unsaved_object(self): - # These tests will catch ValueError in Django 5.0 when passing unsaved - # model instances to related filters becomes forbidden. - # msg = "Model instances passed to related filters must be saved." - msg = "Passing unsaved model instances to related filters is deprecated." + msg = "Model instances passed to related filters must be saved." company = Company.objects.create(name="Django") - with self.assertWarnsMessage(RemovedInDjango50Warning, msg): + with self.assertRaisesMessage(ValueError, msg): Employment.objects.filter(employer=Company(name="unsaved")) - with self.assertWarnsMessage(RemovedInDjango50Warning, msg): + with self.assertRaisesMessage(ValueError, msg): Employment.objects.filter(employer__in=[company, Company(name="unsaved")]) - with self.assertWarnsMessage(RemovedInDjango50Warning, msg): + with self.assertRaisesMessage(ValueError, msg): StaffUser.objects.filter(staff=Staff(name="unsaved")) @@ -3327,28 +3323,14 @@ class ExcludeTests(TestCase): [self.j1, self.j2], ) - @ignore_warnings(category=RemovedInDjango50Warning) - def test_exclude_unsaved_o2o_object(self): - jack = Staff.objects.create(name="jack") - jack_staff = StaffUser.objects.create(staff=jack) - unsaved_object = Staff(name="jane") - - self.assertIsNone(unsaved_object.pk) - self.assertSequenceEqual( - StaffUser.objects.exclude(staff=unsaved_object), [jack_staff] - ) - def test_exclude_unsaved_object(self): - # These tests will catch ValueError in Django 5.0 when passing unsaved - # model instances to related filters becomes forbidden. - # msg = "Model instances passed to related filters must be saved." company = Company.objects.create(name="Django") - msg = "Passing unsaved model instances to related filters is deprecated." - with self.assertWarnsMessage(RemovedInDjango50Warning, msg): + msg = "Model instances passed to related filters must be saved." + with self.assertRaisesMessage(ValueError, msg): Employment.objects.exclude(employer=Company(name="unsaved")) - with self.assertWarnsMessage(RemovedInDjango50Warning, msg): + with self.assertRaisesMessage(ValueError, msg): Employment.objects.exclude(employer__in=[company, Company(name="unsaved")]) - with self.assertWarnsMessage(RemovedInDjango50Warning, msg): + with self.assertRaisesMessage(ValueError, msg): StaffUser.objects.exclude(staff=Staff(name="unsaved"))