diff --git a/django/contrib/gis/static/gis/js/OLMapWidget.js b/django/contrib/gis/static/gis/js/OLMapWidget.js
index a545036c9f..56b33462c6 100644
--- a/django/contrib/gis/static/gis/js/OLMapWidget.js
+++ b/django/contrib/gis/static/gis/js/OLMapWidget.js
@@ -231,3 +231,29 @@ class MapWidget {
document.getElementById(this.options.id).value = jsonFormat.writeGeometry(geometry);
}
}
+
+{
+ function initMapWidgetAfterInlineAdd(event) {
+ const formsetIndex = event.srcElement.id.split('-').pop();
+ event.target.querySelectorAll(".dj_map_wrapper").forEach((wrapper) => {
+ wrapper.querySelector(".dj_map").innerHTML = "";
+ const options = window[wrapper.dataset.widgetoptions];
+ const newOptions = {};
+ for (const key in options) {
+ if (options[key].includes && options[key].includes("__prefix__")) {
+ newOptions[key] = options[key].replace( "__prefix__", formsetIndex);
+ } else {
+ newOptions[key] = options[key];
+ }
+ }
+ new MapWidget(newOptions);
+ });
+ }
+ document.addEventListener("DOMContentLoaded", () => {
+ document.querySelectorAll(".dj_map_wrapper").forEach((wrapper) => {
+ const options = window[wrapper.dataset.widgetoptions];
+ new MapWidget(options);
+ });
+ document.addEventListener('formset:added', initMapWidgetAfterInlineAdd);
+ });
+}
diff --git a/django/contrib/gis/templates/gis/openlayers.html b/django/contrib/gis/templates/gis/openlayers.html
index f9f7e5fa51..7657fa0026 100644
--- a/django/contrib/gis/templates/gis/openlayers.html
+++ b/django/contrib/gis/templates/gis/openlayers.html
@@ -1,6 +1,6 @@
{% load i18n l10n %}
-
+
{% if not disabled %}
{% translate "Delete all Features" %}{% endif %}
{% if display_raw %}
{% translate "Debugging window (serialized value)" %}
{% endif %}
@@ -27,6 +27,6 @@
name: '{{ name }}'
};
{% endblock %}
- var {{ module }} = new MapWidget(options);
+ window.{{ module }}Options = options;
diff --git a/tests/gis_tests/geoadmin/models.py b/tests/gis_tests/geoadmin/models.py
index ea726dd68f..14d1f35015 100644
--- a/tests/gis_tests/geoadmin/models.py
+++ b/tests/gis_tests/geoadmin/models.py
@@ -3,8 +3,19 @@ from django.contrib.gis.db import models
from ..admin import admin
+class Country(models.Model):
+ name = models.CharField(max_length=30)
+
+ class Meta:
+ app_label = "geoadmin"
+
+ def __str__(self):
+ return self.name
+
+
class City(models.Model):
name = models.CharField(max_length=30)
+ country = models.ForeignKey(Country, on_delete=models.CASCADE)
point = models.PointField()
class Meta:
@@ -23,11 +34,20 @@ class CityAdminCustomWidgetKwargs(admin.GISModelAdmin):
}
+class CityInline(admin.TabularInline):
+ model = City
+
+
+class CountryAdmin(admin.ModelAdmin):
+ inlines = [CityInline]
+
+
site = admin.AdminSite(name="gis_admin_modeladmin")
site.register(City, admin.ModelAdmin)
site_gis = admin.AdminSite(name="gis_admin_gismodeladmin")
site_gis.register(City, admin.GISModelAdmin)
+site_gis.register(Country, CountryAdmin)
site_gis_custom = admin.AdminSite(name="gis_admin_gismodeladmin")
site_gis_custom.register(City, CityAdminCustomWidgetKwargs)
diff --git a/tests/gis_tests/geoadmin/tests.py b/tests/gis_tests/geoadmin/tests.py
index e101050464..872d7d6d2b 100644
--- a/tests/gis_tests/geoadmin/tests.py
+++ b/tests/gis_tests/geoadmin/tests.py
@@ -1,16 +1,31 @@
+from django.contrib.admin.tests import AdminSeleniumTestCase
+from django.contrib.auth.models import User
from django.contrib.gis.geos import Point
-from django.test import SimpleTestCase, override_settings
+from django.test import TestCase, modify_settings, override_settings
+from django.test.client import RequestFactory
+from django.urls import reverse
from .models import City, site, site_gis, site_gis_custom
-@override_settings(ROOT_URLCONF="django.contrib.gis.tests.geoadmin.urls")
-class GeoAdminTest(SimpleTestCase):
+@override_settings(
+ ROOT_URLCONF="django.contrib.gis.tests.geoadmin.urls",
+ PASSWORD_HASHERS=["django.contrib.auth.hashers.MD5PasswordHasher"],
+)
+class GeoAdminTest(TestCase):
admin_site = site # ModelAdmin
+ factory = RequestFactory()
+
+ def setUp(self):
+ user = User.objects.create_superuser(
+ username="super", password="secret", email="super@example.com"
+ )
+ self.request = self.factory.get("/admin")
+ self.request.user = user
def test_widget_empty_string(self):
geoadmin = self.admin_site.get_model_admin(City)
- form = geoadmin.get_changelist_form(None)({"point": ""})
+ form = geoadmin.get_changelist_form(self.request)({"point": ""})
with self.assertRaisesMessage(AssertionError, "no logs"):
with self.assertLogs("django.contrib.gis", "ERROR"):
output = str(form["point"])
@@ -22,7 +37,7 @@ class GeoAdminTest(SimpleTestCase):
def test_widget_invalid_string(self):
geoadmin = self.admin_site.get_model_admin(City)
- form = geoadmin.get_changelist_form(None)({"point": "INVALID()"})
+ form = geoadmin.get_changelist_form(self.request)({"point": "INVALID()"})
with self.assertLogs("django.contrib.gis", "ERROR") as cm:
output = str(form["point"])
self.assertInHTML(
@@ -39,7 +54,7 @@ class GeoAdminTest(SimpleTestCase):
def test_widget_has_changed(self):
geoadmin = self.admin_site.get_model_admin(City)
- form = geoadmin.get_changelist_form(None)()
+ form = geoadmin.get_changelist_form(self.request)()
has_changed = form.fields["point"].has_changed
initial = Point(13.4197458572965953, 52.5194108501149799, srid=4326)
@@ -60,7 +75,7 @@ class GISAdminTests(GeoAdminTest):
def test_default_gis_widget_kwargs(self):
geoadmin = self.admin_site.get_model_admin(City)
- form = geoadmin.get_changelist_form(None)()
+ form = geoadmin.get_changelist_form(self.request)()
widget = form["point"].field.widget
self.assertEqual(widget.attrs["default_lat"], 47)
self.assertEqual(widget.attrs["default_lon"], 5)
@@ -68,8 +83,35 @@ class GISAdminTests(GeoAdminTest):
def test_custom_gis_widget_kwargs(self):
geoadmin = site_gis_custom.get_model_admin(City)
- form = geoadmin.get_changelist_form(None)()
+ form = geoadmin.get_changelist_form(self.request)()
widget = form["point"].field.widget
self.assertEqual(widget.attrs["default_lat"], 55)
self.assertEqual(widget.attrs["default_lon"], 37)
self.assertEqual(widget.attrs["default_zoom"], 12)
+
+
+@override_settings(ROOT_URLCONF="gis_tests.geoadmin.urls")
+# GeoDjango admin not yet CSP-compatible with strict values (#25706)
+@modify_settings(MIDDLEWARE={"remove": "django.contrib.admin.tests.CSPMiddleware"})
+class GISSeleniumAdminTests(AdminSeleniumTestCase):
+ available_apps = AdminSeleniumTestCase.available_apps + [
+ "django.contrib.gis",
+ "gis_tests.geoadmin",
+ ]
+
+ def setUp(self):
+ User.objects.create_superuser(
+ username="super", password="secret", email="super@example.com"
+ )
+
+ def test_gis_widget_initalized_when_inline_added(self):
+ from selenium.webdriver.common.by import By
+
+ self.admin_login(username="super", password="secret")
+ self.selenium.get(self.live_server_url + reverse("admin:geoadmin_country_add"))
+ self.assertCountSeleniumElements("tr.dynamic-city_set", 3)
+ add_button = self.selenium.find_element(By.LINK_TEXT, "Add another City")
+ add_button.click()
+ self.assertCountSeleniumElements("tr.dynamic-city_set", 4)
+ map_div = self.selenium.find_element(By.ID, "id_city_set-3-point_map")
+ self.assertCountSeleniumElements(".ol-layer", 1, root_element=map_div)
diff --git a/tests/gis_tests/geoadmin/urls.py b/tests/gis_tests/geoadmin/urls.py
index ce237dbfd1..5843bd1213 100644
--- a/tests/gis_tests/geoadmin/urls.py
+++ b/tests/gis_tests/geoadmin/urls.py
@@ -1,6 +1,7 @@
-from django.contrib import admin
-from django.urls import include, path
+from django.urls import path
+
+from .models import site_gis
urlpatterns = [
- path("admin/", include(admin.site.urls)),
+ path("admin/", site_gis.urls),
]
diff --git a/tests/gis_tests/test_geoforms.py b/tests/gis_tests/test_geoforms.py
index c351edaaad..1589da2fd3 100644
--- a/tests/gis_tests/test_geoforms.py
+++ b/tests/gis_tests/test_geoforms.py
@@ -257,7 +257,8 @@ class SpecializedFieldTest(SimpleTestCase):
"""
self.assertTrue(form_instance.is_valid())
rendered = form_instance.as_p()
- self.assertIn("new MapWidget(options);", rendered)
+ field_name = list(form_instance.fields.keys())[0]
+ self.assertIn(f"window.geodjango_{field_name}Options = options;", rendered)
self.assertIn("map_srid: 3857,", rendered)
self.assertIn("gis/js/OLMapWidget.js", str(form_instance.media))