Loading django/db/backends/base/features.py +1 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ from django.utils.functional import cached_property class BaseDatabaseFeatures(object): gis_enabled = False allows_group_by_pk = False allows_group_by_selected_pks = False # True if django.db.backends.utils.typecast_timestamp is used on values # returned from dates() calls. needs_datetime_string_cast = True Loading django/db/backends/postgresql_psycopg2/features.py +1 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ from django.db.utils import InterfaceError class DatabaseFeatures(BaseDatabaseFeatures): allows_group_by_selected_pks = True needs_datetime_string_cast = False can_return_id_from_insert = True has_real_datatype = True Loading django/db/models/sql/compiler.py +12 −4 Original line number Diff line number Diff line Loading @@ -136,10 +136,7 @@ class SQLCompiler(object): # If the DB can group by primary key, then group by the primary key of # query's main model. Note that for PostgreSQL the GROUP BY clause must # include the primary key of every table, but for MySQL it is enough to # have the main table's primary key. Currently only the MySQL form is # implemented. # MySQLism: however, columns in HAVING clause must be added to the # GROUP BY. # have the main table's primary key. if self.connection.features.allows_group_by_pk: # The logic here is: if the main model's primary key is in the # query, then set new_expressions to that field. If that happens, Loading @@ -150,7 +147,18 @@ class SQLCompiler(object): getattr(expr.output_field, 'model') == self.query.model): pk = expr if pk: # MySQLism: Columns in HAVING clause must be added to the GROUP BY. expressions = [pk] + [expr for expr in expressions if expr in having] elif self.connection.features.allows_group_by_selected_pks: # Filter out all expressions associated with a table's primary key # present in the grouped columns. This is done by identifying all # tables that have their primary key included in the grouped # columns and removing non-primary key columns referring to them. pks = {expr for expr in expressions if hasattr(expr, 'target') and expr.target.primary_key} aliases = {expr.alias for expr in pks} expressions = [ expr for expr in expressions if expr in pks or getattr(expr, 'alias', None) not in aliases ] return expressions def get_select(self): Loading django/test/__init__.py +3 −3 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ from django.test.client import Client, RequestFactory from django.test.testcases import ( TestCase, TransactionTestCase, SimpleTestCase, LiveServerTestCase, skipIfDBFeature, skipUnlessDBFeature skipUnlessAnyDBFeature, skipUnlessDBFeature ) from django.test.utils import (ignore_warnings, modify_settings, override_settings, override_system_checks) Loading @@ -14,8 +14,8 @@ from django.test.utils import (ignore_warnings, modify_settings, __all__ = [ 'Client', 'RequestFactory', 'TestCase', 'TransactionTestCase', 'SimpleTestCase', 'LiveServerTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature', 'ignore_warnings', 'modify_settings', 'override_settings', 'override_system_checks' 'skipUnlessAnyDBFeature', 'skipUnlessDBFeature', 'ignore_warnings', 'modify_settings', 'override_settings', 'override_system_checks' ] # To simplify Django's test suite; not meant as a public API Loading django/test/testcases.py +10 −0 Original line number Diff line number Diff line Loading @@ -1051,6 +1051,16 @@ def skipUnlessDBFeature(*features): ) def skipUnlessAnyDBFeature(*features): """ Skip a test unless a database has any of the named features. """ return _deferredSkip( lambda: not any(getattr(connection.features, feature, False) for feature in features), "Database doesn't support any of the feature(s): %s" % ", ".join(features) ) class QuietWSGIRequestHandler(WSGIRequestHandler): """ Just a regular WSGIRequestHandler except it doesn't log to the standard Loading Loading
django/db/backends/base/features.py +1 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ from django.utils.functional import cached_property class BaseDatabaseFeatures(object): gis_enabled = False allows_group_by_pk = False allows_group_by_selected_pks = False # True if django.db.backends.utils.typecast_timestamp is used on values # returned from dates() calls. needs_datetime_string_cast = True Loading
django/db/backends/postgresql_psycopg2/features.py +1 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ from django.db.utils import InterfaceError class DatabaseFeatures(BaseDatabaseFeatures): allows_group_by_selected_pks = True needs_datetime_string_cast = False can_return_id_from_insert = True has_real_datatype = True Loading
django/db/models/sql/compiler.py +12 −4 Original line number Diff line number Diff line Loading @@ -136,10 +136,7 @@ class SQLCompiler(object): # If the DB can group by primary key, then group by the primary key of # query's main model. Note that for PostgreSQL the GROUP BY clause must # include the primary key of every table, but for MySQL it is enough to # have the main table's primary key. Currently only the MySQL form is # implemented. # MySQLism: however, columns in HAVING clause must be added to the # GROUP BY. # have the main table's primary key. if self.connection.features.allows_group_by_pk: # The logic here is: if the main model's primary key is in the # query, then set new_expressions to that field. If that happens, Loading @@ -150,7 +147,18 @@ class SQLCompiler(object): getattr(expr.output_field, 'model') == self.query.model): pk = expr if pk: # MySQLism: Columns in HAVING clause must be added to the GROUP BY. expressions = [pk] + [expr for expr in expressions if expr in having] elif self.connection.features.allows_group_by_selected_pks: # Filter out all expressions associated with a table's primary key # present in the grouped columns. This is done by identifying all # tables that have their primary key included in the grouped # columns and removing non-primary key columns referring to them. pks = {expr for expr in expressions if hasattr(expr, 'target') and expr.target.primary_key} aliases = {expr.alias for expr in pks} expressions = [ expr for expr in expressions if expr in pks or getattr(expr, 'alias', None) not in aliases ] return expressions def get_select(self): Loading
django/test/__init__.py +3 −3 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ from django.test.client import Client, RequestFactory from django.test.testcases import ( TestCase, TransactionTestCase, SimpleTestCase, LiveServerTestCase, skipIfDBFeature, skipUnlessDBFeature skipUnlessAnyDBFeature, skipUnlessDBFeature ) from django.test.utils import (ignore_warnings, modify_settings, override_settings, override_system_checks) Loading @@ -14,8 +14,8 @@ from django.test.utils import (ignore_warnings, modify_settings, __all__ = [ 'Client', 'RequestFactory', 'TestCase', 'TransactionTestCase', 'SimpleTestCase', 'LiveServerTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature', 'ignore_warnings', 'modify_settings', 'override_settings', 'override_system_checks' 'skipUnlessAnyDBFeature', 'skipUnlessDBFeature', 'ignore_warnings', 'modify_settings', 'override_settings', 'override_system_checks' ] # To simplify Django's test suite; not meant as a public API Loading
django/test/testcases.py +10 −0 Original line number Diff line number Diff line Loading @@ -1051,6 +1051,16 @@ def skipUnlessDBFeature(*features): ) def skipUnlessAnyDBFeature(*features): """ Skip a test unless a database has any of the named features. """ return _deferredSkip( lambda: not any(getattr(connection.features, feature, False) for feature in features), "Database doesn't support any of the feature(s): %s" % ", ".join(features) ) class QuietWSGIRequestHandler(WSGIRequestHandler): """ Just a regular WSGIRequestHandler except it doesn't log to the standard Loading