diff --git a/.github/workflows/test-examples-proj1.yml b/.github/workflows/test-examples-proj1.yml index fda9359..6dae610 100644 --- a/.github/workflows/test-examples-proj1.yml +++ b/.github/workflows/test-examples-proj1.yml @@ -11,7 +11,7 @@ jobs: max-parallel: 5 matrix: python-version: ['3.10'] - django-version: ['2.2', '3.1', '3.2'] + django-version: ['2.2', '3.2', '4.0'] include: - django-version: 'main' python-version: '3.10' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 074c947..e8c391a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,12 @@ jobs: max-parallel: 5 matrix: python-version: ['3.6', '3.7', '3.8', '3.9', '3.10'] - django-version: ['2.2', '3.1', '3.2'] + django-version: ['2.2', '3.2', '4.0'] + exclude: + - django-version: '4.0' + python-version: '3.6' + - django-version: '4.0' + python-version: '3.7' include: - django-version: 'main' python-version: '3.9' diff --git a/CHANGES.rst b/CHANGES.rst index a9eb7c9..c6925bd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -41,7 +41,9 @@ Bug Fixes: Features: * #82 Add Python-3.10 support. +* #98 Add Django-4.0 support. * #82 Drop Django-3.0 support. +* #98 Drop Django-3.1 support. * #90,#13,#8: Support `manage.py inspectdb`, also support working with the django-sql-explorer package. Thanks to Matt Fisher. * #63 Support changing a field from NOT NULL to NULL on migrate / sqlmigrate. diff --git a/django_redshift_backend/base.py b/django_redshift_backend/base.py index f2ee67d..a88c30e 100644 --- a/django_redshift_backend/base.py +++ b/django_redshift_backend/base.py @@ -14,7 +14,7 @@ from django.utils import timezone from django.conf import settings from django.core.exceptions import FieldDoesNotExist -from django.db.backends.base.introspection import FieldInfo +from django.db.backends.base.introspection import FieldInfo, TableInfo from django.db.backends.base.schema import _is_relevant_relation, _related_non_m2m_objects from django.db.backends.base.validation import BaseDatabaseValidation from django.db.backends.ddl_references import Statement @@ -37,6 +37,7 @@ class DatabaseFeatures(BasePGDatabaseFeatures): + minimum_database_version = (8,) # Redshift is postgres 8.0.2 can_return_id_from_insert = False # old name until django-2.x can_return_ids_from_bulk_insert = False # old name until django-2.x can_return_columns_from_insert = False # new name since django-3.0 @@ -234,8 +235,8 @@ def create_model(self, model): # Add any unique_togethers (always deferred, as some fields might # be created afterwards, like geometry fields with some backends) for fields in model._meta.unique_together: - columns = [model._meta.get_field(field).column for field in fields] - self.deferred_sql.append(self._create_unique_sql(model, columns)) + fields = [model._meta.get_field(field) for field in fields] + self.deferred_sql.append(self._create_unique_sql(model, fields)) # Make the table sql = self.sql_create_table % { @@ -517,7 +518,7 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type, self._delete_primary_key(model, strict) # Added a unique? if self._unique_should_be_added(old_field, new_field): - self.execute(self._create_unique_sql(model, [new_field.column])) + self.execute(self._create_unique_sql(model, [new_field])) # Added an index? Add an index if db_index switched to True or a unique # constraint will no longer be used in lieu of an index. The following # lines from the truth table show all True cases; the rest are False: @@ -768,7 +769,7 @@ def _get_max_length(field): )) # 3. add UNIQUE, PKEY, FK again if unique_constraint: - actions.append((self._create_unique_sql(model, [new_field.column], unique_constraint), [])) + actions.append((self._create_unique_sql(model, [new_field], unique_constraint), [])) elif pk_constraint: actions.append((self._create_primary_key_sql(model, new_field), [])) elif fk_constraint: @@ -871,6 +872,34 @@ def remove_field(self, model, field): ) super().remove_field(model, field) + # backwards compatiblity for django + # refs: https://github.com/django/django/pull/14459/files + def _create_unique_sql( + self, model, fields, name=None, condition=None, deferrable=None, + include=None, opclasses=None, expressions=None, + ): + if django.VERSION >= (4,): + return super()._create_unique_sql( + model, fields, name=name, condition=condition, deferrable=deferrable, + include=include, opclasses=opclasses, expressions=expressions + ) + elif django.VERSION >= (3,): # dj32 support + columns = [ + field.column if hasattr(field, 'column') else field + for field in fields + ] + return super()._create_unique_sql( + model, columns, name=name, condition=condition, deferrable=deferrable, + include=include, opclasses=opclasses, + ) + else: # dj32, dj22 support + columns = [ + field.column if hasattr(field, 'column') else field + for field in fields + ] + return super()._create_unique_sql( + model, columns, name=name, condition=condition) + redshift_data_types = { "AutoField": "integer identity(1, 1)", @@ -1050,6 +1079,21 @@ def _get_attribute_number_to_name_map_for_table(self, cursor, table_oid): for _, attnum, attname in cursor.fetchall() } + # BASED FROM https://github.com/django/django/blob/3.2.12/django/db/backends/postgresql/introspection.py#L47-L58 + # Django 4.0 drop old postgres support: https://github.com/django/django/commit/5371342 + def get_table_list(self, cursor): + """Return a list of table and view names in the current database.""" + cursor.execute(""" + SELECT c.relname, + CASE WHEN c.relkind IN ('m', 'v') THEN 'v' ELSE 't' END + FROM pg_catalog.pg_class c + LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace + WHERE c.relkind IN ('f', 'm', 'p', 'r', 'v') + AND n.nspname NOT IN ('pg_catalog', 'pg_toast') + AND pg_catalog.pg_table_is_visible(c.oid) + """) + return [TableInfo(*row) for row in cursor.fetchall() if row[0] not in self.ignored_tables] + class DatabaseWrapper(BasePGDatabaseWrapper): vendor = 'redshift' diff --git a/doc/index.rst b/doc/index.rst index 34aa684..647e036 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -44,7 +44,7 @@ Support versions This product is tested with: * Python-3.6, 3.7, 3.8, 3.9, 3.10 -* Django-2.2, 3.1, 3.2 +* Django-2.2, 3.2, 4.0 License ======= diff --git a/setup.cfg b/setup.cfg index 084e822..fb77abc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,8 +21,8 @@ classifiers = Programming Language :: Python :: 3.10 Framework :: Django Framework :: Django :: 2.2 - Framework :: Django :: 3.1 Framework :: Django :: 3.2 + Framework :: Django :: 4.0 Intended Audience :: Developers Environment :: Plugins Topic :: Software Development :: Libraries :: Python Modules diff --git a/tox.ini b/tox.ini index 8400461..58de487 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = - py{36,37,38,39,310}-dj{22,31,32} - py{39,310}-djmain + py{36,37}-dj{22,32} + py{38,39,310}-dj{22,32,40,main} flake8 check skipsdist = True @@ -17,8 +17,8 @@ python = [gh-actions:env] DJANGO = 2.2: dj22 - 3.1: dj31 3.2: dj32 + 4.0: dj40 main: djmain [testenv] @@ -29,8 +29,8 @@ deps = pytest-cov mock>=2.0 dj22: Django>=2.2,<2.3 - dj31: Django>=3.1,<3.2 dj32: Django>=3.2,<3.3 + dj40: Django>=4.0,<4.1 djmain: https://github.com/django/django/archive/main.tar.gz setenv = DJANGO_SETTINGS_MODULE = settings