Refs #31486 -- Removed ability to pass unsaved model instances to related filters.

Per deprecation timeline.
This commit is contained in:
Mariusz Felisiak 2023-01-12 19:31:49 +01:00
parent 1391356276
commit 4d78d7338c
3 changed files with 12 additions and 38 deletions

View File

@ -1,5 +1,3 @@
import warnings
from django.db.models.lookups import ( from django.db.models.lookups import (
Exact, Exact,
GreaterThan, GreaterThan,
@ -9,7 +7,6 @@ from django.db.models.lookups import (
LessThan, LessThan,
LessThanOrEqual, LessThanOrEqual,
) )
from django.utils.deprecation import RemovedInDjango50Warning
class MultiColSource: class MultiColSource:
@ -45,14 +42,7 @@ def get_normalized_value(value, lhs):
if isinstance(value, Model): if isinstance(value, Model):
if value.pk is None: if value.pk is None:
# When the deprecation ends, replace with: raise ValueError("Model instances passed to related filters must be saved.")
# raise ValueError(
# "Model instances passed to related filters must be saved."
# )
warnings.warn(
"Passing unsaved model instances to related filters is deprecated.",
RemovedInDjango50Warning,
)
value_list = [] value_list = []
sources = lhs.output_field.path_infos[-1].target_fields sources = lhs.output_field.path_infos[-1].target_fields
for source in sources: for source in sources:

View File

@ -322,3 +322,5 @@ to remove usage of these features.
* The usage of ``QuerySet.iterator()`` on a queryset that prefetches related * The usage of ``QuerySet.iterator()`` on a queryset that prefetches related
objects without providing the ``chunk_size`` argument is no longer allowed. objects without providing the ``chunk_size`` argument is no longer allowed.
* Passing unsaved model instances to related filters is no longer allowed.

View File

@ -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.constants import LOUTER
from django.db.models.sql.where import AND, OR, NothingNode, WhereNode from django.db.models.sql.where import AND, OR, NothingNode, WhereNode
from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
from django.test.utils import CaptureQueriesContext, ignore_warnings, register_lookup from django.test.utils import CaptureQueriesContext, register_lookup
from django.utils.deprecation import RemovedInDjango50Warning
from .models import ( from .models import (
FK1, FK1,
@ -1956,16 +1955,13 @@ class Queries5Tests(TestCase):
self.assertEqual(authors.count(), 1) self.assertEqual(authors.count(), 1)
def test_filter_unsaved_object(self): def test_filter_unsaved_object(self):
# These tests will catch ValueError in Django 5.0 when passing unsaved msg = "Model instances passed to related filters must be saved."
# 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."
company = Company.objects.create(name="Django") company = Company.objects.create(name="Django")
with self.assertWarnsMessage(RemovedInDjango50Warning, msg): with self.assertRaisesMessage(ValueError, msg):
Employment.objects.filter(employer=Company(name="unsaved")) 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")]) 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")) StaffUser.objects.filter(staff=Staff(name="unsaved"))
@ -3327,28 +3323,14 @@ class ExcludeTests(TestCase):
[self.j1, self.j2], [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): 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") company = Company.objects.create(name="Django")
msg = "Passing unsaved model instances to related filters is deprecated." msg = "Model instances passed to related filters must be saved."
with self.assertWarnsMessage(RemovedInDjango50Warning, msg): with self.assertRaisesMessage(ValueError, msg):
Employment.objects.exclude(employer=Company(name="unsaved")) 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")]) 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")) StaffUser.objects.exclude(staff=Staff(name="unsaved"))