diff --git a/django/db/models/base.py b/django/db/models/base.py index a89ceafbefb..4a051f8215c 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -788,7 +788,7 @@ class Model(AltersData, metaclass=ModelBase): warnings.warn( f"Passing positional arguments to {method_name}() is deprecated", RemovedInDjango60Warning, - stacklevel=2, + stacklevel=3, ) total_len_args = len(args) + 1 # include self max_len_args = len(defaults) + 1 diff --git a/docs/releases/5.1.1.txt b/docs/releases/5.1.1.txt index b8c29939331..84a61efec54 100644 --- a/docs/releases/5.1.1.txt +++ b/docs/releases/5.1.1.txt @@ -19,3 +19,7 @@ Bugfixes children). A new :class:`~django.contrib.auth.forms.AdminUserCreationForm` including this field was added, isolating the feature to the admin where it was intended (:ticket:`35678`). + +* Adjusted the deprecation warning ``stacklevel`` in :meth:`.Model.save` and + :meth:`.Model.asave` to correctly point to the offending call site + (:ticket:`35060`). diff --git a/tests/basic/tests.py b/tests/basic/tests.py index 6fb67f7e6e8..6d34a958059 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -206,9 +206,10 @@ class ModelInstanceCreationTests(TestCase): def test_save_deprecation(self): a = Article(headline="original", pub_date=datetime(2014, 5, 16)) msg = "Passing positional arguments to save() is deprecated" - with self.assertWarnsMessage(RemovedInDjango60Warning, msg): + with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx: a.save(False, False, None, None) self.assertEqual(Article.objects.count(), 1) + self.assertEqual(ctx.filename, __file__) def test_save_deprecation_positional_arguments_used(self): a = Article() @@ -259,9 +260,10 @@ class ModelInstanceCreationTests(TestCase): async def test_asave_deprecation(self): a = Article(headline="original", pub_date=datetime(2014, 5, 16)) msg = "Passing positional arguments to asave() is deprecated" - with self.assertWarnsMessage(RemovedInDjango60Warning, msg): + with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx: await a.asave(False, False, None, None) self.assertEqual(await Article.objects.acount(), 1) + self.assertEqual(ctx.filename, __file__) async def test_asave_deprecation_positional_arguments_used(self): a = Article() diff --git a/tests/update_only_fields/tests.py b/tests/update_only_fields/tests.py index 816112bc334..a6a5b7cb8eb 100644 --- a/tests/update_only_fields/tests.py +++ b/tests/update_only_fields/tests.py @@ -262,10 +262,11 @@ class UpdateOnlyFieldsTests(TestCase): msg = "Passing positional arguments to save() is deprecated" with ( - self.assertWarnsMessage(RemovedInDjango60Warning, msg), + self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx, self.assertNumQueries(0), ): s.save(False, False, None, []) + self.assertEqual(ctx.filename, __file__) async def test_empty_update_fields_positional_asave(self): s = await Person.objects.acreate(name="Sara", gender="F") @@ -273,8 +274,9 @@ class UpdateOnlyFieldsTests(TestCase): s.name = "Other" msg = "Passing positional arguments to asave() is deprecated" - with self.assertWarnsMessage(RemovedInDjango60Warning, msg): + with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx: await s.asave(False, False, None, []) + self.assertEqual(ctx.filename, __file__) # No save occurred for an empty update_fields. await s.arefresh_from_db()