diff --git a/.github/workflows/schedule_tests.yml b/.github/workflows/schedule_tests.yml index ed3b6b9428..c4d559ec95 100644 --- a/.github/workflows/schedule_tests.yml +++ b/.github/workflows/schedule_tests.yml @@ -104,7 +104,7 @@ jobs: name: Selenium tests, PostgreSQL services: postgres: - image: postgres:14-alpine + image: postgres:15-alpine env: POSTGRES_DB: django POSTGRES_USER: user diff --git a/.github/workflows/selenium.yml b/.github/workflows/selenium.yml index 9348a3550a..9cb71c9143 100644 --- a/.github/workflows/selenium.yml +++ b/.github/workflows/selenium.yml @@ -43,7 +43,7 @@ jobs: name: PostgreSQL services: postgres: - image: postgres:14-alpine + image: postgres:15-alpine env: POSTGRES_DB: django POSTGRES_USER: user diff --git a/django/contrib/gis/db/backends/postgis/operations.py b/django/contrib/gis/db/backends/postgis/operations.py index df3cc7c7ee..5a403fd7fe 100644 --- a/django/contrib/gis/db/backends/postgis/operations.py +++ b/django/contrib/gis/db/backends/postgis/operations.py @@ -204,7 +204,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): raise ImproperlyConfigured( 'Cannot determine PostGIS version for database "%s" ' 'using command "SELECT postgis_lib_version()". ' - "GeoDjango requires at least PostGIS version 3.1. " + "GeoDjango requires at least PostGIS version 3.2. " "Was the database created from a spatial database " "template?" % self.connection.settings_dict["NAME"] ) diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py index 23c31c0929..5bbf4b86cb 100644 --- a/django/db/backends/postgresql/features.py +++ b/django/db/backends/postgresql/features.py @@ -7,7 +7,7 @@ from django.utils.functional import cached_property class DatabaseFeatures(BaseDatabaseFeatures): - minimum_database_version = (14,) + minimum_database_version = (15,) allows_group_by_selected_pks = True can_return_columns_from_insert = True can_return_rows_from_bulk_insert = True @@ -67,6 +67,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_update_conflicts_with_target = True supports_covering_indexes = True supports_stored_generated_columns = True + supports_nulls_distinct_unique_constraints = True can_rename_index = True test_collations = { "deterministic": "C", @@ -155,10 +156,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): "PositiveSmallIntegerField": "SmallIntegerField", } - @cached_property - def is_postgresql_15(self): - return self.connection.pg_version >= 150000 - @cached_property def is_postgresql_16(self): return self.connection.pg_version >= 160000 @@ -172,10 +169,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): return self.connection.pg_version >= 180000 supports_unlimited_charfield = True - supports_nulls_distinct_unique_constraints = property( - operator.attrgetter("is_postgresql_15") - ) - supports_any_value = property(operator.attrgetter("is_postgresql_16")) supports_virtual_generated_columns = property( operator.attrgetter("is_postgresql_18") diff --git a/docs/ref/contrib/gis/install/geolibs.txt b/docs/ref/contrib/gis/install/geolibs.txt index f6dba16906..ab9c8a27dc 100644 --- a/docs/ref/contrib/gis/install/geolibs.txt +++ b/docs/ref/contrib/gis/install/geolibs.txt @@ -17,7 +17,7 @@ Program Description Required `PROJ`_ Cartographic Projections library Yes (PostgreSQL and SQLite only) 9.x, 8.x, 7.x, 6.x :ref:`GDAL ` Geospatial Data Abstraction Library Yes 3.11, 3.10, 3.9, 3.8, 3.7, 3.6, 3.5, 3.4, 3.3, 3.2, 3.1 :ref:`GeoIP ` IP-based geolocation library No 2 -`PostGIS`__ Spatial extensions for PostgreSQL Yes (PostgreSQL only) 3.5, 3.4, 3.3, 3.2, 3.1 +`PostGIS`__ Spatial extensions for PostgreSQL Yes (PostgreSQL only) 3.5, 3.4, 3.3, 3.2 `SpatiaLite`__ Spatial extensions for SQLite Yes (SQLite only) 5.1, 5.0, 4.3 ============================== ==================================== ================================ ======================================================= @@ -44,7 +44,6 @@ totally fine with GeoDjango. Your mileage may vary. GDAL 3.9.0 2024-05-10 GDAL 3.10.0 2024-11-06 GDAL 3.11.0 2025-05-09 - PostGIS 3.1.0 2020-12-18 PostGIS 3.2.0 2021-12-18 PostGIS 3.3.0 2022-08-27 PostGIS 3.4.0 2023-08-15 diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt index 9421723eb8..e08c78b147 100644 --- a/docs/ref/contrib/gis/install/index.txt +++ b/docs/ref/contrib/gis/install/index.txt @@ -57,7 +57,7 @@ supported versions, and any notes for each of the supported database backends: ================== ============================== ================== ========================================= Database Library Requirements Supported Versions Notes ================== ============================== ================== ========================================= -PostgreSQL GEOS, GDAL, PROJ, PostGIS 14+ Requires PostGIS. +PostgreSQL GEOS, GDAL, PROJ, PostGIS 15+ Requires PostGIS. MySQL GEOS, GDAL 8.0.11+ :ref:`Limited functionality `. Oracle GEOS, GDAL 19+ XE not supported. SQLite GEOS, GDAL, PROJ, SpatiaLite 3.31.0+ Requires SpatiaLite 4.3+ @@ -305,7 +305,7 @@ Summary: .. code-block:: shell - $ sudo port install postgresql14-server + $ sudo port install postgresql15-server $ sudo port install geos $ sudo port install proj6 $ sudo port install postgis3 @@ -319,14 +319,14 @@ Summary: .. code-block:: shell - export PATH=/opt/local/bin:/opt/local/lib/postgresql14/bin + export PATH=/opt/local/bin:/opt/local/lib/postgresql15/bin In addition, add the ``DYLD_FALLBACK_LIBRARY_PATH`` setting so that the libraries can be found by Python: .. code-block:: shell - export DYLD_FALLBACK_LIBRARY_PATH=/opt/local/lib:/opt/local/lib/postgresql14 + export DYLD_FALLBACK_LIBRARY_PATH=/opt/local/lib:/opt/local/lib/postgresql15 __ https://www.macports.org/ diff --git a/docs/ref/contrib/postgres/aggregates.txt b/docs/ref/contrib/postgres/aggregates.txt index d2bd48c4fd..1825ec2fc5 100644 --- a/docs/ref/contrib/postgres/aggregates.txt +++ b/docs/ref/contrib/postgres/aggregates.txt @@ -76,7 +76,7 @@ General-purpose aggregation functions .. class:: BitXor(expression, filter=None, default=None, **extra) Returns an ``int`` of the bitwise ``XOR`` of all non-null input values, or - ``default`` if all values are null. It requires PostgreSQL 14+. + ``default`` if all values are null. ``BoolAnd`` ----------- diff --git a/docs/ref/contrib/postgres/constraints.txt b/docs/ref/contrib/postgres/constraints.txt index 4d13eddd24..ee8ef02aa2 100644 --- a/docs/ref/contrib/postgres/constraints.txt +++ b/docs/ref/contrib/postgres/constraints.txt @@ -130,8 +130,7 @@ used for queries that select only included fields (:attr:`~ExclusionConstraint.include`) and filter only by indexed fields (:attr:`~ExclusionConstraint.expressions`). -``include`` is supported for GiST indexes. PostgreSQL 14+ also supports -``include`` for SP-GiST indexes. +``include`` is supported for GiST and SP-GiST indexes. ``violation_error_code`` ------------------------ diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 19af564100..cbd0e2feea 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -119,7 +119,7 @@ below for information on how to set up your database correctly. PostgreSQL notes ================ -Django supports PostgreSQL 14 and higher. `psycopg`_ 3.1.12+ or `psycopg2`_ +Django supports PostgreSQL 15 and higher. `psycopg`_ 3.1.12+ or `psycopg2`_ 2.9.9+ is required, though the latest `psycopg`_ 3.1.12+ is recommended. .. note:: diff --git a/docs/ref/models/constraints.txt b/docs/ref/models/constraints.txt index 34be41962b..9a51c5c7a2 100644 --- a/docs/ref/models/constraints.txt +++ b/docs/ref/models/constraints.txt @@ -273,7 +273,7 @@ creates a unique constraint that only allows one row to store a ``NULL`` value in the ``ordering`` column. Unique constraints with ``nulls_distinct`` are ignored for databases besides -PostgreSQL 15+. +PostgreSQL. ``violation_error_code`` ------------------------ diff --git a/docs/ref/models/indexes.txt b/docs/ref/models/indexes.txt index 6046abf029..d2b2430643 100644 --- a/docs/ref/models/indexes.txt +++ b/docs/ref/models/indexes.txt @@ -214,9 +214,8 @@ See the PostgreSQL documentation for more details about `covering indexes`_. .. admonition:: Restrictions on PostgreSQL - PostgreSQL supports covering B-Tree and :class:`GiST indexes - `. PostgreSQL 14+ also supports - covering :class:`SP-GiST indexes + PostgreSQL supports covering B-Tree, :class:`GiST indexes + `, and :class:`SP-GiST indexes `. .. _covering indexes: https://www.postgresql.org/docs/current/indexes-index-only-scans.html diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index 97a9d05d9d..5e852785d9 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -248,6 +248,17 @@ backends. database has native support for ``DurationField``, override this method to simply return the value. +:mod:`django.contrib.gis` +------------------------- + +* Support for PostGIS 3.1 is removed. + +Dropped support for PostgreSQL 14 +--------------------------------- + +Upstream support for PostgreSQL 14 ends in November 2026. Django 6.1 supports +PostgreSQL 15 and higher. + Miscellaneous ------------- diff --git a/tests/backends/postgresql/tests.py b/tests/backends/postgresql/tests.py index c5fa17041c..f7e7d1e68c 100644 --- a/tests/backends/postgresql/tests.py +++ b/tests/backends/postgresql/tests.py @@ -549,12 +549,12 @@ class Tests(TestCase): def test_get_database_version(self): new_connection = no_pool_connection() - new_connection.pg_version = 140009 - self.assertEqual(new_connection.get_database_version(), (14, 9)) + new_connection.pg_version = 150009 + self.assertEqual(new_connection.get_database_version(), (15, 9)) - @mock.patch.object(connection, "get_database_version", return_value=(13,)) + @mock.patch.object(connection, "get_database_version", return_value=(14,)) def test_check_database_version_supported(self, mocked_get_database_version): - msg = "PostgreSQL 14 or later is required (found 13)." + msg = "PostgreSQL 15 or later is required (found 14)." with self.assertRaisesMessage(NotSupportedError, msg): connection.check_database_version_supported() self.assertTrue(mocked_get_database_version.called)