summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbert Defler <alnw@interia.eu>2022-02-05 20:00:20 +0000
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-02-25 07:51:37 +0100
commit2b6a3baebe70e4d481dd256bdb774ac15e248540 (patch)
treed64a0e39dd47dcb3eff6a4ac9a3a3835829a5c03
parent11cc227344de06ffd0ad7f07c4e4bdd18851adac (diff)
Fixed #31486 -- Deprecated passing unsaved objects to related filters.
Co-Authored-By: Hasan Ramezani <hasan.r67@gmail.com>
-rw-r--r--django/db/models/fields/related_lookups.py12
-rw-r--r--docs/internals/deprecation.txt2
-rw-r--r--docs/releases/4.1.txt3
-rw-r--r--tests/queries/tests.py30
4 files changed, 46 insertions, 1 deletions
diff --git a/django/db/models/fields/related_lookups.py b/django/db/models/fields/related_lookups.py
index 99eadc5da7..17a8622ff9 100644
--- a/django/db/models/fields/related_lookups.py
+++ b/django/db/models/fields/related_lookups.py
@@ -1,3 +1,5 @@
+import warnings
+
from django.db.models.lookups import (
Exact,
GreaterThan,
@@ -7,6 +9,7 @@ from django.db.models.lookups import (
LessThan,
LessThanOrEqual,
)
+from django.utils.deprecation import RemovedInDjango50Warning
class MultiColSource:
@@ -40,6 +43,15 @@ def get_normalized_value(value, lhs):
from django.db.models import Model
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,
+ )
value_list = []
sources = lhs.output_field.path_infos[-1].target_fields
for source in sources:
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index 4818846bea..11f69b1844 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -85,6 +85,8 @@ details on these changes.
objects without providing the ``chunk_size`` argument will no longer be
allowed.
+* Passing unsaved model instances to related filters will no longer be allowed.
+
.. _deprecation-removed-in-4.1:
4.1
diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt
index e3112ad0f5..dafee5271f 100644
--- a/docs/releases/4.1.txt
+++ b/docs/releases/4.1.txt
@@ -484,6 +484,9 @@ Miscellaneous
versions, no prefetching was done. Providing a value for ``chunk_size``
signifies that the additional query per chunk needed to prefetch is desired.
+* Passing unsaved model instances to related filters is deprecated. In Django
+ 5.0, the exception will be raised.
+
Features removed in 4.1
=======================
diff --git a/tests/queries/tests.py b/tests/queries/tests.py
index 594afe25b6..800e71557b 100644
--- a/tests/queries/tests.py
+++ b/tests/queries/tests.py
@@ -12,7 +12,8 @@ from django.db.models.expressions import RawSQL
from django.db.models.sql.constants import LOUTER
from django.db.models.sql.where import NothingNode, WhereNode
from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
-from django.test.utils import CaptureQueriesContext
+from django.test.utils import CaptureQueriesContext, ignore_warnings
+from django.utils.deprecation import RemovedInDjango50Warning
from .models import (
FK1,
@@ -1899,6 +1900,19 @@ class Queries5Tests(TestCase):
self.assertEqual(Ranking.objects.filter(author__in=authors).get(), self.rank3)
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."
+ company = Company.objects.create(name="Django")
+ with self.assertWarnsMessage(RemovedInDjango50Warning, msg):
+ Employment.objects.filter(employer=Company(name="unsaved"))
+ with self.assertWarnsMessage(RemovedInDjango50Warning, msg):
+ Employment.objects.filter(employer__in=[company, Company(name="unsaved")])
+ with self.assertWarnsMessage(RemovedInDjango50Warning, msg):
+ StaffUser.objects.filter(staff=Staff(name="unsaved"))
+
class SelectRelatedTests(TestCase):
def test_tickets_3045_3288(self):
@@ -3211,6 +3225,7 @@ 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)
@@ -3221,6 +3236,19 @@ class ExcludeTests(TestCase):
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):
+ Employment.objects.exclude(employer=Company(name="unsaved"))
+ with self.assertWarnsMessage(RemovedInDjango50Warning, msg):
+ Employment.objects.exclude(employer__in=[company, Company(name="unsaved")])
+ with self.assertWarnsMessage(RemovedInDjango50Warning, msg):
+ StaffUser.objects.exclude(staff=Staff(name="unsaved"))
+
class ExcludeTest17600(TestCase):
"""