From fd705912fff168d10c806568a222dfa25e3bb6a0 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Thu, 18 Sep 2025 15:50:35 -0400 Subject: [PATCH 1/3] Refs #36152, #35667 -- Used skip_file_prefixes in alias deprecation warning. Follow-up to 8ede411a81b40ca53362e6788601193c7e56a0cf. --- django/db/models/sql/query.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index f0865f5f784f..fb1f83104274 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -10,7 +10,6 @@ import copy import difflib import functools -import inspect import sys import warnings from collections import Counter, namedtuple @@ -44,7 +43,7 @@ from django.db.models.sql.constants import INNER, LOUTER, ORDER_DIR, SINGLE from django.db.models.sql.datastructures import BaseTable, Empty, Join, MultiJoin from django.db.models.sql.where import AND, OR, ExtraWhere, NothingNode, WhereNode -from django.utils.deprecation import RemovedInDjango70Warning +from django.utils.deprecation import RemovedInDjango70Warning, django_file_prefixes from django.utils.functional import cached_property from django.utils.regex_helper import _lazy_re_compile from django.utils.tree import Node @@ -1216,15 +1215,10 @@ def join_parent_model(self, opts, model, alias, seen): def check_alias(self, alias): # RemovedInDjango70Warning: When the deprecation ends, remove. if "%" in alias: - if "aggregate" in {frame.function for frame in inspect.stack()}: - stacklevel = 5 - else: - # annotate(), alias(), and values(). - stacklevel = 6 warnings.warn( "Using percent signs in a column alias is deprecated.", - stacklevel=stacklevel, category=RemovedInDjango70Warning, + skip_file_prefixes=django_file_prefixes(), ) if FORBIDDEN_ALIAS_PATTERN.search(alias): raise ValueError( From 5ee651f2555f3258b136f6e8be90f018fd8ffbf0 Mon Sep 17 00:00:00 2001 From: Senthil Kumar Date: Wed, 16 Jul 2025 20:54:21 +0800 Subject: [PATCH 2/3] Fixed #36369 -- Cleared additional cached properties in apps.clear_cache(). Thanks Clifford Gama for the report. Co-authored-by: Jacob Walls --- django/db/models/options.py | 7 +++++++ tests/apps/tests.py | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/django/db/models/options.py b/django/db/models/options.py index 0e229dea3a62..109780ac4941 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -100,6 +100,13 @@ class Options: "managers_map", "base_manager", "default_manager", + "db_returning_fields", + "_property_names", + "pk_fields", + "total_unique_constraints", + "all_parents", + "swapped", + "verbose_name_raw", } REVERSE_PROPERTIES = {"related_objects", "fields_map", "_relation_tree"} diff --git a/tests/apps/tests.py b/tests/apps/tests.py index 0f395b7fc30b..ce7e19be1d08 100644 --- a/tests/apps/tests.py +++ b/tests/apps/tests.py @@ -14,6 +14,7 @@ skipUnlessDBFeature, ) from django.test.utils import extend_sys_path, isolate_apps +from django.utils.functional import cached_property from .models import SoAlternative, TotallyNormal, new_apps from .one_config_app.apps import OneConfig @@ -215,6 +216,27 @@ def test_clear_cache(self): self.assertEqual(apps.get_swappable_settings_name.cache_info().currsize, 0) self.assertEqual(apps.get_models.cache_info().currsize, 0) + @override_settings(INSTALLED_APPS=SOME_INSTALLED_APPS) + def test_cached_properties_cleared_after_cache_clear(self): + opts = apps.get_model("admin", "LogEntry")._meta + + cached_properties = [ + name + for name, attr in models.options.Options.__dict__.items() + if isinstance(attr, cached_property) + ] + + # Access each cached property to populate the cache. + for attr_name in cached_properties: + getattr(opts, attr_name) + self.assertIn(attr_name, opts.__dict__) + + apps.clear_cache() + + for attr_name in cached_properties: + with self.subTest(property=attr_name): + self.assertNotIn(attr_name, opts.__dict__) + @override_settings(INSTALLED_APPS=["apps.apps.RelabeledAppsConfig"]) def test_relabeling(self): self.assertEqual(apps.get_app_config("relabeled").name, "apps") From 336e713e2a1ac143eeec021d66a6f3168f983183 Mon Sep 17 00:00:00 2001 From: Clifford Gama Date: Sun, 14 Sep 2025 15:36:58 +0200 Subject: [PATCH 3/3] Refs #36438 -- Simplified retrieval of GeneratedField base fields in migrations autodetector. This was creating an unusable Q object solely to call referenced_base_fields on it. --- django/db/migrations/autodetector.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index f974c4e4ea2e..7cc22c063707 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -1682,7 +1682,10 @@ def _get_dependencies_for_foreign_key(app_label, model_name, field, project_stat def _get_dependencies_for_generated_field(self, field): dependencies = [] - referenced_base_fields = models.Q(field.expression).referenced_base_fields + referenced_base_fields = [ + name + for name, *lookups in models.Model._get_expr_references(field.expression) + ] newly_added_fields = sorted(self.new_field_keys - self.old_field_keys) for app_label, model_name, added_field_name in newly_added_fields: added_field = self.to_state.models[app_label, model_name].get_field(