diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 8109181855f4..874f525664cd 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -1063,7 +1063,7 @@ def get_distinct(self): opts = self.query.get_meta() for name in self.query.distinct_fields: - parts = name.split(LOOKUP_SEP) + parts = self.query.get_names_to_join(name) _, targets, alias, joins, path, _, transform_function = self._setup_joins( parts, opts, None ) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index d0acb43965b8..270659475627 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -1487,10 +1487,14 @@ def try_transform(self, lhs, name, lookups=None): def get_names_to_join(self, expr): """ - Helper method for the resolution of expressions that could either be a - FilteredRelation alias or a field lookup. + Helper method for the resolution of expressions that could either be an + annotation alias (including FilteredRelation) or a field lookup. """ - return [expr] if expr in self._filtered_relations else expr.split(LOOKUP_SEP) + return ( + [expr] + if (expr in self._filtered_relations or expr in self.annotations) + else expr.split(LOOKUP_SEP) + ) def build_filter( self, diff --git a/docs/howto/custom-model-fields.txt b/docs/howto/custom-model-fields.txt index 4813211e0447..c840338c56f9 100644 --- a/docs/howto/custom-model-fields.txt +++ b/docs/howto/custom-model-fields.txt @@ -671,9 +671,6 @@ This assumes we've imported a ``MyFormField`` field class (which has its own default widget). This document doesn't cover the details of writing custom form fields. -.. _helper functions: ../forms/#generating-forms-for-models -.. _forms documentation: ../forms/ - .. _emulating-built-in-field-types: Emulating built-in field types diff --git a/docs/ref/contrib/sitemaps.txt b/docs/ref/contrib/sitemaps.txt index 8251bd3c8c27..6e14919f5c16 100644 --- a/docs/ref/contrib/sitemaps.txt +++ b/docs/ref/contrib/sitemaps.txt @@ -364,8 +364,6 @@ Here's an example of a :doc:`URLconf ` using ), ] -.. _URLconf: ../url_dispatch/ - Sitemap for static views ======================== diff --git a/docs/ref/templates/language.txt b/docs/ref/templates/language.txt index 38f1f91e9038..bcf4c26ed6e6 100644 --- a/docs/ref/templates/language.txt +++ b/docs/ref/templates/language.txt @@ -28,7 +28,6 @@ or Jinja2_, you should feel right at home with Django's templates. extensions ` to the template language as needed). -.. _`The Django template language: For Python programmers`: ../templates_python/ .. _Smarty: https://www.smarty.net/ .. _Jinja2: https://palletsprojects.com/p/jinja/ diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index 86c6bded7498..974f213a4a45 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -2170,11 +2170,6 @@ Note that, with static (middleware-less) translation, the language is in ``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's in ``request.LANGUAGE_CODE``. -.. _settings file: ../settings/ -.. _middleware documentation: ../middleware/ -.. _session: ../sessions/ -.. _request object: ../request_response/#httprequest-objects - .. _how-django-discovers-translations: How Django discovers translations diff --git a/docs/topics/settings.txt b/docs/topics/settings.txt index fdf93116fbe6..5751060b7446 100644 --- a/docs/topics/settings.txt +++ b/docs/topics/settings.txt @@ -74,8 +74,6 @@ Use the ``--settings`` command-line argument to specify the settings manually: django-admin runserver --settings=mysite.settings -.. _django-admin: ../django-admin/ - On the server (``mod_wsgi``) ---------------------------- diff --git a/tests/backends/oracle/test_creation.py b/tests/backends/oracle/test_creation.py index 87866e50ce80..e6b62b07e829 100644 --- a/tests/backends/oracle/test_creation.py +++ b/tests/backends/oracle/test_creation.py @@ -42,8 +42,8 @@ def patch_execute_statements(self, execute_statements): ) @mock.patch.object(DatabaseCreation, "_test_user_create", return_value=False) - @unittest.skipUnless( - os.environ.get("TESTPILOT_USER") is not None, + @unittest.skipIf( + os.environ.get("TESTPILOT_USERNAME") is not None, "Not possible with Oracle Test Pilot", ) def test_create_test_db(self, *mocked_objects): @@ -67,8 +67,8 @@ def test_create_test_db(self, *mocked_objects): creation._create_test_db(verbosity=0, keepdb=True) @mock.patch.object(DatabaseCreation, "_test_database_create", return_value=False) - @unittest.skipUnless( - os.environ.get("TESTPILOT_USER") is not None, + @unittest.skipIf( + os.environ.get("TESTPILOT_USERNAME") is not None, "Not possible with Oracle Test Pilot", ) def test_create_test_user(self, *mocked_objects): diff --git a/tests/distinct_on_fields/tests.py b/tests/distinct_on_fields/tests.py index e220a1711db2..ca4907cb4844 100644 --- a/tests/distinct_on_fields/tests.py +++ b/tests/distinct_on_fields/tests.py @@ -1,5 +1,5 @@ from django.db import connection -from django.db.models import CharField, F, FloatField, Max +from django.db.models import CharField, F, FloatField, Max, Value from django.db.models.expressions import RawSQL from django.db.models.functions import Lower from django.test import TestCase, skipUnlessDBFeature @@ -312,3 +312,9 @@ def test_disallowed_update_distinct_on(self): msg = "Cannot call update() after .distinct(*fields)." with self.assertRaisesMessage(TypeError, msg): qs.update(name="p4") + + def test_distinct_on_annotation_alias_with_lookup_separator(self): + celebrities = Celebrity.objects.annotate( + celebrity__something=Value(1) + ).distinct("celebrity__something") + self.assertEqual(len(celebrities), 1)