From 54133962319537db5e09dccc74834487abb998d2 Mon Sep 17 00:00:00 2001 From: Javier Matos Odut Date: Wed, 18 Mar 2020 01:01:50 +0100 Subject: [PATCH 1/3] Django 3.x removes six from django.utils. Removing unused imports. --- lib/mysql/connector/django/base.py | 5 ++--- lib/mysql/connector/django/client.py | 1 - lib/mysql/connector/django/compiler.py | 3 +-- lib/mysql/connector/django/creation.py | 1 - lib/mysql/connector/django/features.py | 2 -- lib/mysql/connector/django/introspection.py | 1 - lib/mysql/connector/django/operations.py | 3 +-- lib/mysql/connector/django/schema.py | 1 - lib/mysql/connector/django/validation.py | 2 -- 9 files changed, 4 insertions(+), 15 deletions(-) diff --git a/lib/mysql/connector/django/base.py b/lib/mysql/connector/django/base.py index c137746..cef0fbb 100644 --- a/lib/mysql/connector/django/base.py +++ b/lib/mysql/connector/django/base.py @@ -21,7 +21,7 @@ import sys import warnings -import django +import six from django.core.exceptions import ImproperlyConfigured from django.utils.functional import cached_property @@ -52,10 +52,9 @@ "is required; you have %s" % mysql.connector.__version__) from django.db import utils -from django.db.backends import utils as backend_utils from django.db.backends.base.base import BaseDatabaseWrapper from django.db.backends.signals import connection_created -from django.utils import (six, timezone, dateparse) +from django.utils import (timezone, dateparse) from django.conf import settings from mysql.connector.django.client import DatabaseClient diff --git a/lib/mysql/connector/django/client.py b/lib/mysql/connector/django/client.py index 07b3144..4e1da38 100644 --- a/lib/mysql/connector/django/client.py +++ b/lib/mysql/connector/django/client.py @@ -1,6 +1,5 @@ # MySQL Connector/Python - MySQL driver written in Python. -import django import subprocess from django.db.backends.base.client import BaseDatabaseClient diff --git a/lib/mysql/connector/django/compiler.py b/lib/mysql/connector/django/compiler.py index 076f703..8145430 100644 --- a/lib/mysql/connector/django/compiler.py +++ b/lib/mysql/connector/django/compiler.py @@ -1,9 +1,8 @@ # MySQL Connector/Python - MySQL driver written in Python. -import django from django.db.models.sql import compiler -from django.utils.six.moves import zip_longest +from six.moves import zip_longest class SQLCompiler(compiler.SQLCompiler): diff --git a/lib/mysql/connector/django/creation.py b/lib/mysql/connector/django/creation.py index 5580308..9a7de2d 100644 --- a/lib/mysql/connector/django/creation.py +++ b/lib/mysql/connector/django/creation.py @@ -1,6 +1,5 @@ # MySQL Connector/Python - MySQL driver written in Python. -import django from django.db import models from django.db.backends.base.creation import BaseDatabaseCreation diff --git a/lib/mysql/connector/django/features.py b/lib/mysql/connector/django/features.py index 47d53da..774749a 100644 --- a/lib/mysql/connector/django/features.py +++ b/lib/mysql/connector/django/features.py @@ -1,9 +1,7 @@ # MySQL Connector/Python - MySQL driver written in Python. -import django from django.db.backends.base.features import BaseDatabaseFeatures from django.utils.functional import cached_property -from django.utils import six try: import pytz diff --git a/lib/mysql/connector/django/introspection.py b/lib/mysql/connector/django/introspection.py index 4de6043..a10f834 100644 --- a/lib/mysql/connector/django/introspection.py +++ b/lib/mysql/connector/django/introspection.py @@ -4,7 +4,6 @@ import re from collections import namedtuple -import django from django.db.backends.base.introspection import ( BaseDatabaseIntrospection, FieldInfo, TableInfo ) diff --git a/lib/mysql/connector/django/operations.py b/lib/mysql/connector/django/operations.py index 798acd3..4e7c447 100644 --- a/lib/mysql/connector/django/operations.py +++ b/lib/mysql/connector/django/operations.py @@ -4,10 +4,9 @@ import uuid -import django from django.conf import settings from django.db.backends.base.operations import BaseDatabaseOperations -from django.utils import six, timezone +from django.utils import timezone from django.utils.encoding import force_text try: diff --git a/lib/mysql/connector/django/schema.py b/lib/mysql/connector/django/schema.py index 90cb10a..1eaace1 100644 --- a/lib/mysql/connector/django/schema.py +++ b/lib/mysql/connector/django/schema.py @@ -1,6 +1,5 @@ # MySQL Connector/Python - MySQL driver written in Python. -import django from django.db.backends.base.schema import BaseDatabaseSchemaEditor from django.db.models import NOT_PROVIDED diff --git a/lib/mysql/connector/django/validation.py b/lib/mysql/connector/django/validation.py index 24ca6ea..5d7f35d 100644 --- a/lib/mysql/connector/django/validation.py +++ b/lib/mysql/connector/django/validation.py @@ -1,7 +1,5 @@ # MySQL Connector/Python - MySQL driver written in Python. -import django - from django.db.backends.base.validation import BaseDatabaseValidation from django.core import checks from django.db import connection From b7a5ada3cac2b8d34d3e465d2fde34836ba48dad Mon Sep 17 00:00:00 2001 From: Javier Matos Odut Date: Mon, 23 Mar 2020 13:44:51 +0100 Subject: [PATCH 2/3] Just replace DatabaseOperations to mimic Django native DatabaseOperations. --- lib/mysql/connector/django/operations.py | 226 +---------------------- 1 file changed, 2 insertions(+), 224 deletions(-) diff --git a/lib/mysql/connector/django/operations.py b/lib/mysql/connector/django/operations.py index 4e7c447..15609e2 100644 --- a/lib/mysql/connector/django/operations.py +++ b/lib/mysql/connector/django/operations.py @@ -2,12 +2,8 @@ from __future__ import unicode_literals -import uuid +from django.db.backends.mysql.operations import DatabaseOperations as MySQLDatabaseOperations -from django.conf import settings -from django.db.backends.base.operations import BaseDatabaseOperations -from django.utils import timezone -from django.utils.encoding import force_text try: from _mysql_connector import datetime_to_mysql, time_to_mysql @@ -17,223 +13,5 @@ HAVE_CEXT = True -class DatabaseOperations(BaseDatabaseOperations): +class DatabaseOperations(MySQLDatabaseOperations): compiler_module = "mysql.connector.django.compiler" - - # MySQL stores positive fields as UNSIGNED ints. - integer_field_ranges = dict(BaseDatabaseOperations.integer_field_ranges, - PositiveSmallIntegerField=(0, 4294967295), - PositiveIntegerField=( - 0, 18446744073709551615),) - - def date_extract_sql(self, lookup_type, field_name): - # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html - if lookup_type == 'week_day': - # DAYOFWEEK() returns an integer, 1-7, Sunday=1. - # Note: WEEKDAY() returns 0-6, Monday=0. - return "DAYOFWEEK({0})".format(field_name) - else: - return "EXTRACT({0} FROM {1})".format( - lookup_type.upper(), field_name) - - def date_trunc_sql(self, lookup_type, field_name): - """Returns SQL simulating DATE_TRUNC - - This function uses MySQL functions DATE_FORMAT and CAST to - simulate DATE_TRUNC. - - The field_name is returned when lookup_type is not supported. - """ - fields = ['year', 'month', 'day', 'hour', 'minute', 'second'] - format = ('%Y-', '%m', '-%d', ' %H:', '%i', ':%S') - format_def = ('0000-', '01', '-01', ' 00:', '00', ':00') - try: - i = fields.index(lookup_type) + 1 - except ValueError: - # Wrong lookup type, just return the value from MySQL as-is - sql = field_name - else: - format_str = ''.join([f for f in format[:i]] + - [f for f in format_def[i:]]) - sql = "CAST(DATE_FORMAT({0}, '{1}') AS DATETIME)".format( - field_name, format_str) - return sql - - def datetime_extract_sql(self, lookup_type, field_name, tzname): - if settings.USE_TZ: - field_name = "CONVERT_TZ({0}, 'UTC', %s)".format(field_name) - params = [tzname] - else: - params = [] - - # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html - if lookup_type == 'week_day': - # DAYOFWEEK() returns an integer, 1-7, Sunday=1. - # Note: WEEKDAY() returns 0-6, Monday=0. - sql = "DAYOFWEEK({0})".format(field_name) - else: - sql = "EXTRACT({0} FROM {1})".format(lookup_type.upper(), - field_name) - return sql, params - - def datetime_trunc_sql(self, lookup_type, field_name, tzname): - if settings.USE_TZ: - field_name = "CONVERT_TZ({0}, 'UTC', %s)".format(field_name) - params = [tzname] - else: - params = [] - fields = ['year', 'month', 'day', 'hour', 'minute', 'second'] - format_ = ('%Y-', '%m', '-%d', ' %H:', '%i', ':%S') - format_def = ('0000-', '01', '-01', ' 00:', '00', ':00') - try: - i = fields.index(lookup_type) + 1 - except ValueError: - sql = field_name - else: - format_str = ''.join([f for f in format_[:i]] + - [f for f in format_def[i:]]) - sql = "CAST(DATE_FORMAT({0}, '{1}') AS DATETIME)".format( - field_name, format_str) - return sql, params - - def date_interval_sql(self, timedelta): - """Returns SQL for calculating date/time intervals - """ - return "INTERVAL '%d 0:0:%d:%d' DAY_MICROSECOND" % ( - timedelta.days, timedelta.seconds, timedelta.microseconds), [] - - def format_for_duration_arithmetic(self, sql): - if self.connection.features.supports_microsecond_precision: - return 'INTERVAL %s MICROSECOND' % sql - else: - return 'INTERVAL FLOOR(%s / 1000000) SECOND' % sql - - def drop_foreignkey_sql(self): - return "DROP FOREIGN KEY" - - def force_no_ordering(self): - """ - "ORDER BY NULL" prevents MySQL from implicitly ordering by grouped - columns. If no ordering would otherwise be applied, we don't want any - implicit sorting going on. - """ - return [(None, ("NULL", [], False))] - - def fulltext_search_sql(self, field_name): - return 'MATCH ({0}) AGAINST (%s IN BOOLEAN MODE)'.format(field_name) - - def last_executed_query(self, cursor, sql, params): - return force_text(cursor.statement, errors='replace') - - def no_limit_value(self): - # 2**64 - 1, as recommended by the MySQL documentation - return 18446744073709551615 - - def quote_name(self, name): - if name.startswith("`") and name.endswith("`"): - return name # Quoting once is enough. - return "`{0}`".format(name) - - def random_function_sql(self): - return 'RAND()' - - def sql_flush(self, style, tables, sequences, allow_cascade=False): - if tables: - sql = ['SET FOREIGN_KEY_CHECKS = 0;'] - for table in tables: - sql.append('{keyword} {table};'.format( - keyword=style.SQL_KEYWORD('TRUNCATE'), - table=style.SQL_FIELD(self.quote_name(table)))) - sql.append('SET FOREIGN_KEY_CHECKS = 1;') - sql.extend(self.sequence_reset_by_name_sql(style, sequences)) - return sql - else: - return [] - - def validate_autopk_value(self, value): - # MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653. - if value == 0: - raise ValueError('The database backend does not accept 0 as a ' - 'value for AutoField.') - return value - - def adapt_datetimefield_value(self, value): - return self.value_to_db_datetime(value) - - def value_to_db_datetime(self, value): - if value is None: - return None - # MySQL doesn't support tz-aware times - if timezone.is_aware(value): - if settings.USE_TZ: - value = value.astimezone(timezone.utc).replace(tzinfo=None) - else: - raise ValueError( - "MySQL backend does not support timezone-aware times." - ) - if not self.connection.features.supports_microsecond_precision: - value = value.replace(microsecond=0) - if not self.connection.use_pure: - return datetime_to_mysql(value) - return self.connection.converter.to_mysql(value) - - def adapt_timefield_value(self, value): - return self.value_to_db_time(value) - - def value_to_db_time(self, value): - if value is None: - return None - - # MySQL doesn't support tz-aware times - if timezone.is_aware(value): - raise ValueError("MySQL backend does not support timezone-aware " - "times.") - - if not self.connection.use_pure: - return time_to_mysql(value) - return self.connection.converter.to_mysql(value) - - def max_name_length(self): - return 64 - - def bulk_insert_sql(self, fields, placeholder_rows): - placeholder_rows_sql = (", ".join(row) for row in placeholder_rows) - values_sql = ", ".join("({0})".format(sql) for sql in placeholder_rows_sql) - return "VALUES " + values_sql - - def combine_expression(self, connector, sub_expressions): - """ - MySQL requires special cases for ^ operators in query expressions - """ - if connector == '^': - return 'POW(%s)' % ','.join(sub_expressions) - return super(DatabaseOperations, self).combine_expression( - connector, sub_expressions) - - def get_db_converters(self, expression): - converters = super(DatabaseOperations, self).get_db_converters( - expression) - internal_type = expression.output_field.get_internal_type() - if internal_type in ['BooleanField', 'NullBooleanField']: - converters.append(self.convert_booleanfield_value) - if internal_type == 'UUIDField': - converters.append(self.convert_uuidfield_value) - if internal_type == 'TextField': - converters.append(self.convert_textfield_value) - return converters - - def convert_booleanfield_value(self, value, - expression, connection, context): - if value in (0, 1): - value = bool(value) - return value - - def convert_uuidfield_value(self, value, expression, connection, context): - if value is not None: - value = uuid.UUID(value) - return value - - def convert_textfield_value(self, value, expression, connection, context): - if value is not None: - value = force_text(value) - return value From ba9b33d4bd5ce33ae32b51ac89f9e40a1562fc42 Mon Sep 17 00:00:00 2001 From: Javier Matos Odut Date: Mon, 23 Mar 2020 14:26:15 +0100 Subject: [PATCH 3/3] Overriding some methods to customize the adapter. --- lib/mysql/connector/django/operations.py | 39 ++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/lib/mysql/connector/django/operations.py b/lib/mysql/connector/django/operations.py index 15609e2..f9af274 100644 --- a/lib/mysql/connector/django/operations.py +++ b/lib/mysql/connector/django/operations.py @@ -2,7 +2,9 @@ from __future__ import unicode_literals +from django.conf import settings from django.db.backends.mysql.operations import DatabaseOperations as MySQLDatabaseOperations +from django.utils import timezone try: @@ -15,3 +17,40 @@ class DatabaseOperations(MySQLDatabaseOperations): compiler_module = "mysql.connector.django.compiler" + + def adapt_datetimefield_value(self, value): + if value is None: + return None + + # MySQL doesn't support tz-aware times + if timezone.is_aware(value): + if settings.USE_TZ: + value = timezone.make_naive(value, self.connection.timezone) + else: + raise ValueError("MySQL backend does not support timezone-aware datetimes when USE_TZ is False.") + + if not self.connection.features.supports_microsecond_precision: + value = value.replace(microsecond=0) + if not self.connection.use_pure: + return datetime_to_mysql(value) + + return self.connection.converter.to_mysql(value) + + def adapt_timefield_value(self, value): + if value is None: + return None + + # MySQL doesn't support tz-aware times + if timezone.is_aware(value): + raise ValueError("MySQL backend does not support timezone-aware times.") + + if not self.connection.use_pure: + return time_to_mysql(value) + + return self.connection.converter.to_mysql(value) + + def convert_datetimefield_value(self, value, expression, connection): + if value is not None: + if not timezone.is_aware(value): + value = timezone.make_aware(value, self.connection.timezone) + return value