Browse Source

Fixed #30056 -- Added SQLite support for StdDev and Variance functions.

Nick Pope 6 năm trước cách đây
mục cha
commit
83677faf86

+ 1 - 11
django/db/backends/base/features.py

@@ -1,5 +1,4 @@
-from django.db.models.aggregates import StdDev
-from django.db.utils import NotSupportedError, ProgrammingError
+from django.db.utils import ProgrammingError
 from django.utils.functional import cached_property
 
 
@@ -298,12 +297,3 @@ class BaseDatabaseFeatures:
             count, = cursor.fetchone()
             cursor.execute('DROP TABLE ROLLBACK_TEST')
         return count == 0
-
-    @cached_property
-    def supports_stddev(self):
-        """Confirm support for STDDEV and related stats functions."""
-        try:
-            self.connection.ops.check_expression_support(StdDev(1))
-        except NotSupportedError:
-            return False
-        return True

+ 13 - 0
django/db/backends/sqlite3/base.py

@@ -7,6 +7,7 @@ import functools
 import math
 import operator
 import re
+import statistics
 import warnings
 from itertools import chain
 from sqlite3 import dbapi2 as Database
@@ -49,6 +50,14 @@ def none_guard(func):
     return wrapper
 
 
+def list_aggregate(function):
+    """
+    Return an aggregate class that accumulates values in a list and applies
+    the provided function to the data.
+    """
+    return type('ListAggregate', (list,), {'finalize': function, 'step': list.append})
+
+
 Database.register_converter("bool", b'1'.__eq__)
 Database.register_converter("time", decoder(parse_time))
 Database.register_converter("datetime", decoder(parse_datetime))
@@ -210,6 +219,10 @@ class DatabaseWrapper(BaseDatabaseWrapper):
         conn.create_function('SIN', 1, none_guard(math.sin))
         conn.create_function('SQRT', 1, none_guard(math.sqrt))
         conn.create_function('TAN', 1, none_guard(math.tan))
+        conn.create_aggregate('STDDEV_POP', 1, list_aggregate(statistics.pstdev))
+        conn.create_aggregate('STDDEV_SAMP', 1, list_aggregate(statistics.stdev))
+        conn.create_aggregate('VAR_POP', 1, list_aggregate(statistics.pvariance))
+        conn.create_aggregate('VAR_SAMP', 1, list_aggregate(statistics.variance))
         conn.execute('PRAGMA foreign_keys = ON')
         return conn
 

+ 0 - 21
django/db/backends/sqlite3/features.py

@@ -1,8 +1,6 @@
 import sys
 
-from django.db import utils
 from django.db.backends.base.features import BaseDatabaseFeatures
-from django.utils.functional import cached_property
 
 from .base import Database
 
@@ -41,22 +39,3 @@ class DatabaseFeatures(BaseDatabaseFeatures):
     # reasonably performant way.
     supports_pragma_foreign_key_check = Database.sqlite_version_info >= (3, 20, 0)
     can_defer_constraint_checks = supports_pragma_foreign_key_check
-
-    @cached_property
-    def supports_stddev(self):
-        """
-        Confirm support for STDDEV and related stats functions.
-
-        SQLite supports STDDEV as an extension package; so
-        connection.ops.check_expression_support() can't unilaterally
-        rule out support for STDDEV. Manually check whether the call works.
-        """
-        with self.connection.cursor() as cursor:
-            cursor.execute('CREATE TABLE STDDEV_TEST (X INT)')
-            try:
-                cursor.execute('SELECT STDDEV(*) FROM STDDEV_TEST')
-                has_support = True
-            except utils.DatabaseError:
-                has_support = False
-            cursor.execute('DROP TABLE STDDEV_TEST')
-        return has_support

+ 4 - 12
docs/ref/models/querysets.txt

@@ -3400,12 +3400,9 @@ by the aggregate.
         By default, ``StdDev`` returns the population standard deviation. However,
         if ``sample=True``, the return value will be the sample standard deviation.
 
-    .. admonition:: SQLite
+    .. versionchanged:: 2.2
 
-        SQLite doesn't provide ``StdDev`` out of the box. An implementation
-        is available as an extension module for SQLite. Consult the `SQLite
-        documentation`_ for instructions on obtaining and installing this
-        extension.
+        SQLite support was added.
 
 ``Sum``
 ~~~~~~~
@@ -3434,14 +3431,9 @@ by the aggregate.
         By default, ``Variance`` returns the population variance. However,
         if ``sample=True``, the return value will be the sample variance.
 
-    .. admonition:: SQLite
+    .. versionchanged:: 2.2
 
-        SQLite doesn't provide ``Variance`` out of the box. An implementation
-        is available as an extension module for SQLite. Consult the `SQLite
-        documentation`_ for instructions on obtaining and installing this
-        extension.
-
-.. _SQLite documentation: https://www.sqlite.org/contrib
+        SQLite support was added.
 
 Query-related tools
 ===================

+ 3 - 0
docs/releases/2.2.txt

@@ -235,6 +235,9 @@ Models
   ``Model.delete()``. This improves the performance of autocommit by reducing
   the number of database round trips.
 
+* Added SQLite support for the :class:`~django.db.models.StdDev` and
+  :class:`~django.db.models.Variance` functions.
+
 Requests and Responses
 ~~~~~~~~~~~~~~~~~~~~~~
 

+ 0 - 1
tests/aggregation_regress/tests.py

@@ -1116,7 +1116,6 @@ class AggregationTests(TestCase):
             lambda b: (b.name, b.authorCount)
         )
 
-    @skipUnlessDBFeature('supports_stddev')
     def test_stddev(self):
         self.assertEqual(
             Book.objects.aggregate(StdDev('pages')),

+ 0 - 1
tests/backends/tests.py

@@ -340,7 +340,6 @@ class BackendTestCase(TransactionTestCase):
 
     def test_cached_db_features(self):
         self.assertIn(connection.features.supports_transactions, (True, False))
-        self.assertIn(connection.features.supports_stddev, (True, False))
         self.assertIn(connection.features.can_introspect_foreign_keys, (True, False))
 
     def test_duplicate_table_error(self):