Repository: django/django
File: django/db/backends/sqlite3/operations.py (Python)

Code before:


class DatabaseOperations(BaseDatabaseOperations):
    cast_char_field_without_max_length = "text"
    cast_data_types = {
        "DateField": "TEXT",
        "DateTimeField": "TEXT",
    }
    explain_prefix = "EXPLAIN QUERY PLAN"
    # List of datatypes to that cannot be extracted with JSON_EXTRACT() on
    # SQLite. Use JSON_TYPE() instead.
    jsonfield_datatype_values = frozenset(["null", "false", "true"])

    def check_expression_support(self, expression):
        if isinstance(expression, UNSUPPORTED_DATETIME_AGGREGATES):
            for expr in expression.get_source_expressions():
                try:
                    output_field = expr.output_field
                except (AttributeError, FieldError):
                    # Not every subexpression has an output_field which is fine
                    # to ignore.
                    pass
                else:
                    if isinstance(output_field, DATETIME_FIELDS):
                        raise NotSupportedError(
                            "You cannot use Sum, Avg, StdDev, and Variance "
                            "aggregations on date/time fields in sqlite3 "
                            "since date/time is saved as text."
                        )
        if (
            isinstance(expression, models.Aggregate)
            and expression.distinct
            and len(expression.source_expressions) > 1
        ):
            raise NotSupportedError(
                "SQLite doesn't support DISTINCT on aggregate functions "
                "accepting multiple arguments."
            )

    def date_extract_sql(self, lookup_type, sql, params):
        """
        Support EXTRACT with a user-defined function django_date_extract()
        that's registered in connect(). Use single quotes because this is a
        string and could otherwise cause a collision with a field name.
        """
        return f"django_date_extract(%s, {sql})", (lookup_type.lower(), *params)

    def format_for_duration_arithmetic(self, sql):
        """Do nothing since formatting is handled in the custom function."""
        return sql


Review comment (refactor):
Suggestion: The error message duplicates which aggregates aren't allowed. Should that be driven by the new constant too? `django.utils.text.get_text_list` should be helpful here.

Code after:


class DatabaseOperations(BaseDatabaseOperations):
    cast_char_field_without_max_length = "text"
    cast_data_types = {
        "DateField": "TEXT",
        "DateTimeField": "TEXT",
    }
    explain_prefix = "EXPLAIN QUERY PLAN"
    # List of datatypes to that cannot be extracted with JSON_EXTRACT() on
    # SQLite. Use JSON_TYPE() instead.
    jsonfield_datatype_values = frozenset(["null", "false", "true"])

    def check_expression_support(self, expression):
        if isinstance(expression, UNSUPPORTED_DATETIME_AGGREGATES):
            for expr in expression.get_source_expressions():
                try:
                    output_field = expr.output_field
                except (AttributeError, FieldError):
                    # Not every subexpression has an output_field which is fine
                    # to ignore.
                    pass
                else:
                    if isinstance(output_field, DATETIME_FIELDS):
                        klass = expression.__class__.__name__
                        raise NotSupportedError(
                            f"SQLite does not support {klass} on date or time "
                            "fields, because they are stored as text."
                        )
        if (
            isinstance(expression, models.Aggregate)
            and expression.distinct
            and len(expression.source_expressions) > 1
        ):
            raise NotSupportedError(
                "SQLite doesn't support DISTINCT on aggregate functions "
                "accepting multiple arguments."
            )

    def date_extract_sql(self, lookup_type, sql, params):
        """
        Support EXTRACT with a user-defined function django_date_extract()
        that's registered in connect(). Use single quotes because this is a
        string and could otherwise cause a collision with a field name.
        """
        return f"django_date_extract(%s, {sql})", (lookup_type.lower(), *params)

    def format_for_duration_arithmetic(self, sql):
        """Do nothing since formatting is handled in the custom function."""
        return sql