1
0
mirror of https://github.com/django/django.git synced 2024-12-22 17:16:24 +00:00

Fixed #30049 -- Initialized GIS widgets when admin inlines are added.

This commit is contained in:
Claude Paroz 2024-08-17 17:52:15 +02:00
parent a57596e443
commit a02b4ce19f
6 changed files with 104 additions and 14 deletions

View File

@ -231,3 +231,29 @@ class MapWidget {
document.getElementById(this.options.id).value = jsonFormat.writeGeometry(geometry); 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);
});
}

View File

@ -1,6 +1,6 @@
{% load i18n l10n %} {% load i18n l10n %}
<div id="{{ id }}_div_map" class="dj_map_wrapper"> <div id="{{ id }}_div_map" class="dj_map_wrapper" data-widgetoptions="{{ module }}Options">
<div id="{{ id }}_map" class="dj_map"></div> <div id="{{ id }}_map" class="dj_map"></div>
{% if not disabled %}<span class="clear_features"><a href="">{% translate "Delete all Features" %}</a></span>{% endif %} {% if not disabled %}<span class="clear_features"><a href="">{% translate "Delete all Features" %}</a></span>{% endif %}
{% if display_raw %}<p>{% translate "Debugging window (serialized value)" %}</p>{% endif %} {% if display_raw %}<p>{% translate "Debugging window (serialized value)" %}</p>{% endif %}
@ -27,6 +27,6 @@
name: '{{ name }}' name: '{{ name }}'
}; };
{% endblock %} {% endblock %}
var {{ module }} = new MapWidget(options); window.{{ module }}Options = options;
</script> </script>
</div> </div>

View File

@ -3,8 +3,19 @@ from django.contrib.gis.db import models
from ..admin import admin 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): class City(models.Model):
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
point = models.PointField() point = models.PointField()
class Meta: 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 = admin.AdminSite(name="gis_admin_modeladmin")
site.register(City, admin.ModelAdmin) site.register(City, admin.ModelAdmin)
site_gis = admin.AdminSite(name="gis_admin_gismodeladmin") site_gis = admin.AdminSite(name="gis_admin_gismodeladmin")
site_gis.register(City, 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 = admin.AdminSite(name="gis_admin_gismodeladmin")
site_gis_custom.register(City, CityAdminCustomWidgetKwargs) site_gis_custom.register(City, CityAdminCustomWidgetKwargs)

View File

@ -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.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 from .models import City, site, site_gis, site_gis_custom
@override_settings(ROOT_URLCONF="django.contrib.gis.tests.geoadmin.urls") @override_settings(
class GeoAdminTest(SimpleTestCase): ROOT_URLCONF="django.contrib.gis.tests.geoadmin.urls",
PASSWORD_HASHERS=["django.contrib.auth.hashers.MD5PasswordHasher"],
)
class GeoAdminTest(TestCase):
admin_site = site # ModelAdmin 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): def test_widget_empty_string(self):
geoadmin = self.admin_site.get_model_admin(City) 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.assertRaisesMessage(AssertionError, "no logs"):
with self.assertLogs("django.contrib.gis", "ERROR"): with self.assertLogs("django.contrib.gis", "ERROR"):
output = str(form["point"]) output = str(form["point"])
@ -22,7 +37,7 @@ class GeoAdminTest(SimpleTestCase):
def test_widget_invalid_string(self): def test_widget_invalid_string(self):
geoadmin = self.admin_site.get_model_admin(City) 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: with self.assertLogs("django.contrib.gis", "ERROR") as cm:
output = str(form["point"]) output = str(form["point"])
self.assertInHTML( self.assertInHTML(
@ -39,7 +54,7 @@ class GeoAdminTest(SimpleTestCase):
def test_widget_has_changed(self): def test_widget_has_changed(self):
geoadmin = self.admin_site.get_model_admin(City) 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 has_changed = form.fields["point"].has_changed
initial = Point(13.4197458572965953, 52.5194108501149799, srid=4326) initial = Point(13.4197458572965953, 52.5194108501149799, srid=4326)
@ -60,7 +75,7 @@ class GISAdminTests(GeoAdminTest):
def test_default_gis_widget_kwargs(self): def test_default_gis_widget_kwargs(self):
geoadmin = self.admin_site.get_model_admin(City) 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 widget = form["point"].field.widget
self.assertEqual(widget.attrs["default_lat"], 47) self.assertEqual(widget.attrs["default_lat"], 47)
self.assertEqual(widget.attrs["default_lon"], 5) self.assertEqual(widget.attrs["default_lon"], 5)
@ -68,8 +83,35 @@ class GISAdminTests(GeoAdminTest):
def test_custom_gis_widget_kwargs(self): def test_custom_gis_widget_kwargs(self):
geoadmin = site_gis_custom.get_model_admin(City) 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 widget = form["point"].field.widget
self.assertEqual(widget.attrs["default_lat"], 55) self.assertEqual(widget.attrs["default_lat"], 55)
self.assertEqual(widget.attrs["default_lon"], 37) self.assertEqual(widget.attrs["default_lon"], 37)
self.assertEqual(widget.attrs["default_zoom"], 12) 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)

View File

@ -1,6 +1,7 @@
from django.contrib import admin from django.urls import path
from django.urls import include, path
from .models import site_gis
urlpatterns = [ urlpatterns = [
path("admin/", include(admin.site.urls)), path("admin/", site_gis.urls),
] ]

View File

@ -257,7 +257,8 @@ class SpecializedFieldTest(SimpleTestCase):
""" """
self.assertTrue(form_instance.is_valid()) self.assertTrue(form_instance.is_valid())
rendered = form_instance.as_p() 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("map_srid: 3857,", rendered)
self.assertIn("gis/js/OLMapWidget.js", str(form_instance.media)) self.assertIn("gis/js/OLMapWidget.js", str(form_instance.media))