diff --git a/django/contrib/gis/db/backends/postgis/operations.py b/django/contrib/gis/db/backends/postgis/operations.py index b68db377f82..17d7b3213d9 100644 --- a/django/contrib/gis/db/backends/postgis/operations.py +++ b/django/contrib/gis/db/backends/postgis/operations.py @@ -203,7 +203,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 2.5. " + "GeoDjango requires at least PostGIS version 3.0. " "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 0a0ade4ced3..c4398c9ad31 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 = (12,) + minimum_database_version = (13,) allows_group_by_selected_pks = True can_return_columns_from_insert = True can_return_rows_from_bulk_insert = True @@ -126,10 +126,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): "PositiveSmallIntegerField": "SmallIntegerField", } - @cached_property - def is_postgresql_13(self): - return self.connection.pg_version >= 130000 - @cached_property def is_postgresql_14(self): return self.connection.pg_version >= 140000 diff --git a/docs/ref/contrib/gis/install/geolibs.txt b/docs/ref/contrib/gis/install/geolibs.txt index 8a666d49b55..90ad6ed10e6 100644 --- a/docs/ref/contrib/gis/install/geolibs.txt +++ b/docs/ref/contrib/gis/install/geolibs.txt @@ -12,7 +12,7 @@ Program Description Required `PROJ`_ Cartographic Projections library Yes (PostgreSQL and SQLite only) 9.x, 8.x, 7.x, 6.x, 5.x :doc:`GDAL <../gdal>` Geospatial Data Abstraction Library Yes 3.7, 3.6, 3.5, 3.4, 3.3, 3.2, 3.1, 3.0, 2.4 :doc:`GeoIP <../geoip2>` IP-based geolocation library No 2 -`PostGIS`__ Spatial extensions for PostgreSQL Yes (PostgreSQL only) 3.4, 3.3, 3.2, 3.1, 3.0, 2.5 +`PostGIS`__ Spatial extensions for PostgreSQL Yes (PostgreSQL only) 3.4, 3.3, 3.2, 3.1, 3.0 `SpatiaLite`__ Spatial extensions for SQLite Yes (SQLite only) 5.0, 4.3 ======================== ==================================== ================================ =========================================== @@ -35,7 +35,6 @@ totally fine with GeoDjango. Your mileage may vary. GDAL 3.5.0 2022-05-13 GDAL 3.6.0 2022-11-03 GDAL 3.7.0 2023-05-10 - PostGIS 2.5.0 2018-09-23 PostGIS 3.0.0 2019-10-20 PostGIS 3.1.0 2020-12-18 PostGIS 3.2.0 2021-12-18 diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt index c5d03eee0de..e5c2c17bd56 100644 --- a/docs/ref/contrib/gis/install/index.txt +++ b/docs/ref/contrib/gis/install/index.txt @@ -56,7 +56,7 @@ supported versions, and any notes for each of the supported database backends: ================== ============================== ================== ========================================= Database Library Requirements Supported Versions Notes ================== ============================== ================== ========================================= -PostgreSQL GEOS, GDAL, PROJ, PostGIS 12+ Requires PostGIS. +PostgreSQL GEOS, GDAL, PROJ, PostGIS 13+ Requires PostGIS. MySQL GEOS, GDAL 8.0.11+ :ref:`Limited functionality `. Oracle GEOS, GDAL 19+ XE not supported. SQLite GEOS, GDAL, PROJ, SpatiaLite 3.27.0+ Requires SpatiaLite 4.3+ diff --git a/docs/ref/contrib/gis/install/postgis.txt b/docs/ref/contrib/gis/install/postgis.txt index 4a828817d10..9264b69a12b 100644 --- a/docs/ref/contrib/gis/install/postgis.txt +++ b/docs/ref/contrib/gis/install/postgis.txt @@ -30,7 +30,7 @@ Post-installation Creating a spatial database --------------------------- -PostGIS 2 includes an extension for PostgreSQL that's used to enable spatial +PostGIS includes an extension for PostgreSQL that's used to enable spatial functionality: .. code-block:: shell @@ -50,9 +50,9 @@ process. An alternative is to use a migration operation in your project:: class Migration(migrations.Migration): operations = [CreateExtension("postgis"), ...] -If you plan to use PostGIS raster functionality on PostGIS 3+, you should also -activate the ``postgis_raster`` extension. You can install the extension using -the :class:`~django.contrib.postgres.operations.CreateExtension` migration +If you plan to use PostGIS raster functionality, you should also activate the +``postgis_raster`` extension. You can install the extension using the +:class:`~django.contrib.postgres.operations.CreateExtension` migration operation, or directly by running ``CREATE EXTENSION postgis_raster;``. GeoDjango does not currently leverage any `PostGIS topology functionality`__. diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 62f82974b0a..8c5f877dfc3 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -114,7 +114,7 @@ below for information on how to set up your database correctly. PostgreSQL notes ================ -Django supports PostgreSQL 12 and higher. `psycopg`_ 3.1.8+ or `psycopg2`_ +Django supports PostgreSQL 13 and higher. `psycopg`_ 3.1.8+ or `psycopg2`_ 2.8.4+ is required, though the latest `psycopg`_ 3.1.8+ is recommended. .. note:: diff --git a/docs/releases/5.1.txt b/docs/releases/5.1.txt index 2232c83d490..5d8e9b6993f 100644 --- a/docs/releases/5.1.txt +++ b/docs/releases/5.1.txt @@ -228,12 +228,23 @@ backends. * ... +:mod:`django.contrib.gis` +------------------------- + +* Support for PostGIS 2.5 is removed. + Dropped support for MariaDB 10.4 -------------------------------- Upstream support for MariaDB 10.4 ends in June 2024. Django 5.1 supports MariaDB 10.5 and higher. +Dropped support for PostgreSQL 12 +--------------------------------- + +Upstream support for PostgreSQL 12 ends in November 2024. Django 5.1 supports +PostgreSQL 13 and higher. + Miscellaneous ------------- diff --git a/tests/backends/postgresql/tests.py b/tests/backends/postgresql/tests.py index 590dbe6073d..31aac022c86 100644 --- a/tests/backends/postgresql/tests.py +++ b/tests/backends/postgresql/tests.py @@ -404,12 +404,12 @@ class Tests(TestCase): def test_get_database_version(self): new_connection = connection.copy() - new_connection.pg_version = 110009 - self.assertEqual(new_connection.get_database_version(), (11, 9)) + new_connection.pg_version = 130009 + self.assertEqual(new_connection.get_database_version(), (13, 9)) - @mock.patch.object(connection, "get_database_version", return_value=(11,)) + @mock.patch.object(connection, "get_database_version", return_value=(12,)) def test_check_database_version_supported(self, mocked_get_database_version): - msg = "PostgreSQL 12 or later is required (found 11)." + msg = "PostgreSQL 13 or later is required (found 12)." with self.assertRaisesMessage(NotSupportedError, msg): connection.check_database_version_supported() self.assertTrue(mocked_get_database_version.called) diff --git a/tests/gis_tests/gis_migrations/migrations/0001_setup_extensions.py b/tests/gis_tests/gis_migrations/migrations/0001_setup_extensions.py index 227d6b668de..96569abfacf 100644 --- a/tests/gis_tests/gis_migrations/migrations/0001_setup_extensions.py +++ b/tests/gis_tests/gis_migrations/migrations/0001_setup_extensions.py @@ -3,16 +3,10 @@ from django.db import connection, migrations if connection.features.supports_raster: from django.contrib.postgres.operations import CreateExtension - pg_version = connection.ops.postgis_version_tuple() - class Migration(migrations.Migration): - # PostGIS 3+ requires postgis_raster extension. - if pg_version[1:] >= (3,): - operations = [ - CreateExtension("postgis_raster"), - ] - else: - operations = [] + operations = [ + CreateExtension("postgis_raster"), + ] else: diff --git a/tests/gis_tests/rasterapp/migrations/0001_setup_extensions.py b/tests/gis_tests/rasterapp/migrations/0001_setup_extensions.py index 227d6b668de..96569abfacf 100644 --- a/tests/gis_tests/rasterapp/migrations/0001_setup_extensions.py +++ b/tests/gis_tests/rasterapp/migrations/0001_setup_extensions.py @@ -3,16 +3,10 @@ from django.db import connection, migrations if connection.features.supports_raster: from django.contrib.postgres.operations import CreateExtension - pg_version = connection.ops.postgis_version_tuple() - class Migration(migrations.Migration): - # PostGIS 3+ requires postgis_raster extension. - if pg_version[1:] >= (3,): - operations = [ - CreateExtension("postgis_raster"), - ] - else: - operations = [] + operations = [ + CreateExtension("postgis_raster"), + ] else: diff --git a/tests/postgres_tests/migrations/0001_setup_extensions.py b/tests/postgres_tests/migrations/0001_setup_extensions.py index 8045610aefb..8fbc3e190e1 100644 --- a/tests/postgres_tests/migrations/0001_setup_extensions.py +++ b/tests/postgres_tests/migrations/0001_setup_extensions.py @@ -1,6 +1,6 @@ from unittest import mock -from django.db import connection, migrations +from django.db import migrations try: from django.contrib.postgres.operations import ( @@ -9,7 +9,6 @@ try: BtreeGistExtension, CITextExtension, CreateExtension, - CryptoExtension, HStoreExtension, TrigramExtension, UnaccentExtension, @@ -23,11 +22,6 @@ except ImportError: HStoreExtension = mock.Mock() TrigramExtension = mock.Mock() UnaccentExtension = mock.Mock() - needs_crypto_extension = False -else: - needs_crypto_extension = ( - connection.vendor == "postgresql" and not connection.features.is_postgresql_13 - ) class Migration(migrations.Migration): @@ -39,8 +33,6 @@ class Migration(migrations.Migration): # Ensure CreateExtension quotes extension names by creating one with a # dash in its name. CreateExtension("uuid-ossp"), - # CryptoExtension is required for RandomUUID() on PostgreSQL < 13. - CryptoExtension() if needs_crypto_extension else mock.Mock(), HStoreExtension(), TrigramExtension(), UnaccentExtension(), diff --git a/tests/queries/test_explain.py b/tests/queries/test_explain.py index 11684356b98..f04153754bf 100644 --- a/tests/queries/test_explain.py +++ b/tests/queries/test_explain.py @@ -83,9 +83,8 @@ class ExplainTests(TestCase): {"verbose": False, "timing": False, "analyze": True}, {"summary": True}, {"settings": True}, + {"analyze": True, "wal": True}, ] - if connection.features.is_postgresql_13: - test_options.append({"analyze": True, "wal": True}) for options in test_options: with self.subTest(**options), transaction.atomic(): with CaptureQueriesContext(connection) as captured_queries: