diff --git a/django/db/backends/base/base.py b/django/db/backends/base/base.py index df378b30d5..14b2623414 100644 --- a/django/db/backends/base/base.py +++ b/django/db/backends/base/base.py @@ -120,6 +120,12 @@ class BaseDatabaseWrapper: self.ops = self.ops_class(self) self.validation = self.validation_class(self) + def __repr__(self): + return ( + f'<{self.__class__.__qualname__} ' + f'vendor={self.vendor!r} alias={self.alias!r}>' + ) + def ensure_timezone(self): """ Ensure the connection's timezone is set to `self.timezone_name` and diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 32657a3e40..73cf2c5f62 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -44,6 +44,13 @@ class SQLCompiler: self.klass_info = None self._meta_ordering = None + def __repr__(self): + return ( + f'<{self.__class__.__qualname__} ' + f'model={self.query.model.__qualname__} ' + f'connection={self.connection!r} using={self.using!r}>' + ) + def setup_query(self): if all(self.query.alias_refcount[a] == 0 for a in self.query.alias_map): self.query.get_initial_alias() diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 2c5f11cbbf..faca57da56 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -2393,6 +2393,12 @@ class JoinPromoter: # inner and/or outer joins. self.votes = Counter() + def __repr__(self): + return ( + f'{self.__class__.__qualname__}(connector={self.connector!r}, ' + f'num_children={self.num_children!r}, negated={self.negated!r})' + ) + def add_votes(self, votes): """ Add single vote per item to self.votes. Parameter can be any diff --git a/tests/backends/base/test_base.py b/tests/backends/base/test_base.py index f89aec57f0..b07d6d3ceb 100644 --- a/tests/backends/base/test_base.py +++ b/tests/backends/base/test_base.py @@ -9,6 +9,13 @@ from ..models import Square class DatabaseWrapperTests(SimpleTestCase): + def test_repr(self): + conn = connections[DEFAULT_DB_ALIAS] + self.assertEqual( + repr(conn), + f"", + ) + def test_initialization_class_attributes(self): """ The "initialization" class attributes like client_class and diff --git a/tests/queries/test_query.py b/tests/queries/test_query.py index 523fa607f0..6ed766e3ae 100644 --- a/tests/queries/test_query.py +++ b/tests/queries/test_query.py @@ -6,7 +6,7 @@ from django.db.models.expressions import Col, Func from django.db.models.fields.related_lookups import RelatedIsNull from django.db.models.functions import Lower from django.db.models.lookups import Exact, GreaterThan, IsNull, LessThan -from django.db.models.sql.query import Query +from django.db.models.sql.query import JoinPromoter, Query from django.db.models.sql.where import OR from django.test import SimpleTestCase from django.test.utils import register_lookup @@ -150,3 +150,11 @@ class TestQuery(SimpleTestCase): msg = 'Cannot filter against a non-conditional expression.' with self.assertRaisesMessage(TypeError, msg): query.build_where(Func(output_field=CharField())) + + +class JoinPromoterTest(SimpleTestCase): + def test_repr(self): + self.assertEqual( + repr(JoinPromoter('AND', 3, True)), + "JoinPromoter(connector='AND', num_children=3, negated=True)", + ) diff --git a/tests/queries/test_sqlcompiler.py b/tests/queries/test_sqlcompiler.py new file mode 100644 index 0000000000..3116429c05 --- /dev/null +++ b/tests/queries/test_sqlcompiler.py @@ -0,0 +1,17 @@ +from django.db import DEFAULT_DB_ALIAS, connection +from django.db.models.sql import Query +from django.test import SimpleTestCase + +from .models import Item + + +class SQLCompilerTest(SimpleTestCase): + def test_repr(self): + query = Query(Item) + compiler = query.get_compiler(DEFAULT_DB_ALIAS, connection) + self.assertEqual( + repr(compiler), + f" " + f"using='default'>" + )