Fixed #28107 -- Added DatabaseFeatures.allows_group_by_selected_pks_on_model() to allow enabling optimization for unmanaged models.
This commit is contained in:
parent
10d5e439e9
commit
b1d37fea8f
|
@ -313,3 +313,8 @@ class BaseDatabaseFeatures:
|
||||||
count, = cursor.fetchone()
|
count, = cursor.fetchone()
|
||||||
cursor.execute('DROP TABLE ROLLBACK_TEST')
|
cursor.execute('DROP TABLE ROLLBACK_TEST')
|
||||||
return count == 0
|
return count == 0
|
||||||
|
|
||||||
|
def allows_group_by_selected_pks_on_model(self, model):
|
||||||
|
if not self.allows_group_by_selected_pks:
|
||||||
|
return False
|
||||||
|
return model._meta.managed
|
||||||
|
|
|
@ -171,7 +171,11 @@ class SQLCompiler:
|
||||||
# database views on which the optimization might not be allowed.
|
# database views on which the optimization might not be allowed.
|
||||||
pks = {
|
pks = {
|
||||||
expr for expr in expressions
|
expr for expr in expressions
|
||||||
if hasattr(expr, 'target') and expr.target.primary_key and expr.target.model._meta.managed
|
if (
|
||||||
|
hasattr(expr, 'target') and
|
||||||
|
expr.target.primary_key and
|
||||||
|
self.connection.features.allows_group_by_selected_pks_on_model(expr.target.model)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
aliases = {expr.alias for expr in pks}
|
aliases = {expr.alias for expr in pks}
|
||||||
expressions = [
|
expressions = [
|
||||||
|
|
|
@ -355,6 +355,17 @@ Models
|
||||||
|
|
||||||
* :class:`~django.db.models.CheckConstraint` is now supported on MySQL 8.0.16+.
|
* :class:`~django.db.models.CheckConstraint` is now supported on MySQL 8.0.16+.
|
||||||
|
|
||||||
|
* The new ``allows_group_by_selected_pks_on_model()`` method of
|
||||||
|
``django.db.backends.base.BaseDatabaseFeatures`` allows optimization of
|
||||||
|
``GROUP BY`` clauses to require only the selected models' primary keys. By
|
||||||
|
default, it's supported only for managed models on PostgreSQL.
|
||||||
|
|
||||||
|
To enable the ``GROUP BY`` primary key-only optimization for unmanaged
|
||||||
|
models, you have to subclass the PostgreSQL database engine, overriding the
|
||||||
|
features class ``allows_group_by_selected_pks_on_model()`` method as you
|
||||||
|
require. See :ref:`Subclassing the built-in database backends
|
||||||
|
<subclassing-database-backends>` for an example.
|
||||||
|
|
||||||
Requests and Responses
|
Requests and Responses
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -1333,6 +1333,33 @@ class AggregationTests(TestCase):
|
||||||
self.assertIn(field.name, grouping[index + 1][0])
|
self.assertIn(field.name, grouping[index + 1][0])
|
||||||
assertQuerysetResults(queryset)
|
assertQuerysetResults(queryset)
|
||||||
|
|
||||||
|
@skipUnlessDBFeature('allows_group_by_selected_pks')
|
||||||
|
def test_aggregate_unmanaged_model_as_tables(self):
|
||||||
|
qs = Book.objects.select_related('contact').annotate(num_authors=Count('authors'))
|
||||||
|
# Force treating unmanaged models as tables.
|
||||||
|
with mock.patch(
|
||||||
|
'django.db.connection.features.allows_group_by_selected_pks_on_model',
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
with mock.patch.object(Book._meta, 'managed', False), \
|
||||||
|
mock.patch.object(Author._meta, 'managed', False):
|
||||||
|
_, _, grouping = qs.query.get_compiler(using='default').pre_sql_setup()
|
||||||
|
self.assertEqual(len(grouping), 2)
|
||||||
|
self.assertIn('id', grouping[0][0])
|
||||||
|
self.assertIn('id', grouping[1][0])
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
qs.order_by('name'),
|
||||||
|
[
|
||||||
|
('Artificial Intelligence: A Modern Approach', 2),
|
||||||
|
('Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 1),
|
||||||
|
('Practical Django Projects', 1),
|
||||||
|
('Python Web Development with Django', 3),
|
||||||
|
('Sams Teach Yourself Django in 24 Hours', 1),
|
||||||
|
('The Definitive Guide to Django: Web Development Done Right', 2),
|
||||||
|
],
|
||||||
|
attrgetter('name', 'num_authors'),
|
||||||
|
)
|
||||||
|
|
||||||
def test_reverse_join_trimming(self):
|
def test_reverse_join_trimming(self):
|
||||||
qs = Author.objects.annotate(Count('book_contact_set__contact'))
|
qs = Author.objects.annotate(Count('book_contact_set__contact'))
|
||||||
self.assertIn(' JOIN ', str(qs.query))
|
self.assertIn(' JOIN ', str(qs.query))
|
||||||
|
|
Loading…
Reference in New Issue