mirror of
https://github.com/django/django.git
synced 2025-06-14 16:09:12 +00:00
Fixed #25706 -- Refactored geometry widgets to remove inline JavaScript.
Refactored GIS-related JavaScript initialization to eliminate inline scripts from templates. Added support for specifying a base layer using the new `base_layer_name` attribute on `BaseGeometryWidget`, allowing custom map tile providers via user-defined JavaScript. As a result, the `gis/openlayers-osm.html` template was removed. Thanks Sarah Boyce for reviews. Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
This commit is contained in:
parent
e80b33ae4d
commit
f2f6046c0f
@ -1,11 +1,9 @@
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.gis import gdal
|
||||
from django.contrib.gis.geometry import json_regex
|
||||
from django.contrib.gis.geos import GEOSException, GEOSGeometry
|
||||
from django.forms.widgets import Widget
|
||||
from django.utils import translation
|
||||
|
||||
logger = logging.getLogger("django.contrib.gis")
|
||||
|
||||
@ -16,6 +14,7 @@ class BaseGeometryWidget(Widget):
|
||||
Render a map using the WKT of the geometry.
|
||||
"""
|
||||
|
||||
base_layer = None
|
||||
geom_type = "GEOMETRY"
|
||||
map_srid = 4326
|
||||
display_raw = False
|
||||
@ -24,9 +23,10 @@ class BaseGeometryWidget(Widget):
|
||||
template_name = "" # set on subclasses
|
||||
|
||||
def __init__(self, attrs=None):
|
||||
self.attrs = {}
|
||||
for key in ("geom_type", "map_srid", "display_raw"):
|
||||
self.attrs[key] = getattr(self, key)
|
||||
self.attrs = {
|
||||
key: getattr(self, key)
|
||||
for key in ("base_layer", "geom_type", "map_srid", "display_raw")
|
||||
}
|
||||
if attrs:
|
||||
self.attrs.update(attrs)
|
||||
|
||||
@ -61,26 +61,16 @@ class BaseGeometryWidget(Widget):
|
||||
self.map_srid,
|
||||
err,
|
||||
)
|
||||
|
||||
context["serialized"] = self.serialize(value)
|
||||
geom_type = gdal.OGRGeomType(self.attrs["geom_type"]).name
|
||||
context.update(
|
||||
self.build_attrs(
|
||||
self.attrs,
|
||||
{
|
||||
"name": name,
|
||||
"module": "geodjango_%s" % name.replace("-", "_"), # JS-safe
|
||||
"serialized": self.serialize(value),
|
||||
"geom_type": "Geometry" if geom_type == "Unknown" else geom_type,
|
||||
"STATIC_URL": settings.STATIC_URL,
|
||||
"LANGUAGE_BIDI": translation.get_language_bidi(),
|
||||
**(attrs or {}),
|
||||
},
|
||||
)
|
||||
context["widget"]["attrs"]["geom_name"] = (
|
||||
"Geometry" if geom_type == "Unknown" else geom_type
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class OpenLayersWidget(BaseGeometryWidget):
|
||||
base_layer = "nasaWorldview"
|
||||
template_name = "gis/openlayers.html"
|
||||
map_srid = 3857
|
||||
|
||||
@ -112,14 +102,15 @@ class OSMWidget(OpenLayersWidget):
|
||||
An OpenLayers/OpenStreetMap-based widget.
|
||||
"""
|
||||
|
||||
template_name = "gis/openlayers-osm.html"
|
||||
base_layer = "osm"
|
||||
default_lon = 5
|
||||
default_lat = 47
|
||||
default_zoom = 12
|
||||
|
||||
def __init__(self, attrs=None):
|
||||
super().__init__()
|
||||
for key in ("default_lon", "default_lat", "default_zoom"):
|
||||
self.attrs[key] = getattr(self, key)
|
||||
if attrs:
|
||||
self.attrs.update(attrs)
|
||||
if attrs is None:
|
||||
attrs = {}
|
||||
attrs.setdefault("default_lon", self.default_lon)
|
||||
attrs.setdefault("default_lat", self.default_lat)
|
||||
attrs.setdefault("default_zoom", self.default_zoom)
|
||||
super().__init__(attrs=attrs)
|
||||
|
@ -58,8 +58,16 @@ class MapWidget {
|
||||
this.options[property] = options[property];
|
||||
}
|
||||
}
|
||||
if (!options.base_layer) {
|
||||
this.options.base_layer = new ol.layer.Tile({source: new ol.source.OSM()});
|
||||
|
||||
// Options' base_layer can be empty, or contain a layerBuilder key, or
|
||||
// be a layer already constructed.
|
||||
const base_layer = options.base_layer;
|
||||
if (typeof base_layer === 'string' && base_layer in MapWidget.layerBuilder) {
|
||||
this.baseLayer = MapWidget.layerBuilder[base_layer]();
|
||||
} else if (base_layer && typeof base_layer !== 'string') {
|
||||
this.baseLayer = base_layer;
|
||||
} else {
|
||||
this.baseLayer = MapWidget.layerBuilder.osm();
|
||||
}
|
||||
|
||||
this.map = this.createMap();
|
||||
@ -120,7 +128,7 @@ class MapWidget {
|
||||
createMap() {
|
||||
return new ol.Map({
|
||||
target: this.options.map_id,
|
||||
layers: [this.options.base_layer],
|
||||
layers: [this.baseLayer],
|
||||
view: new ol.View({
|
||||
zoom: this.options.default_zoom
|
||||
})
|
||||
@ -231,3 +239,43 @@ class MapWidget {
|
||||
document.getElementById(this.options.id).value = jsonFormat.writeGeometry(geometry);
|
||||
}
|
||||
}
|
||||
|
||||
// Static property assignment (ES6-compatible)
|
||||
MapWidget.layerBuilder = {
|
||||
nasaWorldview: () => {
|
||||
return new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
attributions: "NASA Worldview",
|
||||
maxZoom: 8,
|
||||
url: "https://map1{a-c}.vis.earthdata.nasa.gov/wmts-webmerc/" +
|
||||
"BlueMarble_ShadedRelief_Bathymetry/default/%7BTime%7D/" +
|
||||
"GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg"
|
||||
})
|
||||
});
|
||||
},
|
||||
osm: () => {
|
||||
return new ol.layer.Tile({source: new ol.source.OSM()});
|
||||
}
|
||||
};
|
||||
|
||||
function initMapWidgetInSection(section) {
|
||||
const maps = [];
|
||||
|
||||
section.querySelectorAll(".dj_map_wrapper").forEach((wrapper) => {
|
||||
// Avoid initializing map widget on an empty form.
|
||||
if (wrapper.id.includes('__prefix__')) {
|
||||
return;
|
||||
}
|
||||
const options = JSON.parse(wrapper.querySelector("#mapwidget-options").textContent);
|
||||
options.id = wrapper.querySelector("textarea").id;
|
||||
options.map_id = wrapper.querySelector(".dj_map").id;
|
||||
maps.push(new MapWidget(options));
|
||||
});
|
||||
|
||||
return maps;
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
initMapWidgetInSection(document);
|
||||
document.addEventListener('formset:added', (ev) => {initMapWidgetInSection(ev.target);});
|
||||
});
|
||||
|
@ -1,12 +0,0 @@
|
||||
{% extends "gis/openlayers.html" %}
|
||||
{% load l10n %}
|
||||
|
||||
{% block options %}{{ block.super }}
|
||||
options['default_lon'] = {{ default_lon|unlocalize }};
|
||||
options['default_lat'] = {{ default_lat|unlocalize }};
|
||||
options['default_zoom'] = {{ default_zoom|unlocalize }};
|
||||
{% endblock %}
|
||||
|
||||
{% block base_layer %}
|
||||
var base_layer = new ol.layer.Tile({source: new ol.source.OSM()});
|
||||
{% endblock %}
|
@ -1,32 +1,10 @@
|
||||
{% load i18n l10n %}
|
||||
{% load i18n %}
|
||||
|
||||
<div id="{{ id }}_div_map" class="dj_map_wrapper">
|
||||
<div id="{{ id }}_map" class="dj_map"></div>
|
||||
<div id="{{ widget.attrs.id }}_div_map" class="dj_map_wrapper">
|
||||
<div id="{{ widget.attrs.id }}_map" class="dj_map"></div>
|
||||
{% 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 %}
|
||||
<textarea id="{{ id }}" class="vSerializedField required" cols="150" rows="10" name="{{ name }}"
|
||||
{% if not display_raw %} hidden{% endif %}>{{ serialized }}</textarea>
|
||||
<script>
|
||||
{% block base_layer %}
|
||||
var base_layer = new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
attributions: "NASA Worldview",
|
||||
maxZoom: 8,
|
||||
url: "https://map1{a-c}.vis.earthdata.nasa.gov/wmts-webmerc/" +
|
||||
"BlueMarble_ShadedRelief_Bathymetry/default/%7BTime%7D/" +
|
||||
"GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg"
|
||||
})
|
||||
});
|
||||
{% endblock %}
|
||||
{% block options %}var options = {
|
||||
base_layer: base_layer,
|
||||
geom_name: '{{ geom_type }}',
|
||||
id: '{{ id }}',
|
||||
map_id: '{{ id }}_map',
|
||||
map_srid: {{ map_srid|unlocalize }},
|
||||
name: '{{ name }}'
|
||||
};
|
||||
{% endblock %}
|
||||
var {{ module }} = new MapWidget(options);
|
||||
</script>
|
||||
{% if widget.attrs.display_raw %}<p>{% translate "Debugging window (serialized value)" %}</p>{% endif %}
|
||||
<textarea id="{{ widget.attrs.id }}" class="vSerializedField required" cols="150" rows="10" name="{{ widget.name }}"
|
||||
{% if not widget.attrs.display_raw %} hidden{% endif %}>{{ serialized }}</textarea>
|
||||
{{ widget.attrs|json_script:"mapwidget-options" }}
|
||||
</div>
|
||||
|
@ -96,6 +96,14 @@ Widget attributes
|
||||
GeoDjango widgets are template-based, so their attributes are mostly different
|
||||
from other Django widget attributes.
|
||||
|
||||
.. attribute:: BaseGeometryWidget.base_layer
|
||||
|
||||
.. versionadded:: 6.0
|
||||
|
||||
A string that specifies the identifier for the default base map layer to be
|
||||
used by the corresponding JavaScript map widget. It is passed as part of
|
||||
the widget options when rendering, allowing the ``MapWidget`` to determine
|
||||
which map tile provider or base layer to initialize (default is ``None``).
|
||||
|
||||
.. attribute:: BaseGeometryWidget.geom_type
|
||||
|
||||
@ -137,15 +145,29 @@ Widget classes
|
||||
|
||||
This is an abstract base widget containing the logic needed by subclasses.
|
||||
You cannot directly use this widget for a geometry field.
|
||||
Note that the rendering of GeoDjango widgets is based on a template,
|
||||
identified by the :attr:`template_name` class attribute.
|
||||
Note that the rendering of GeoDjango widgets is based on a base layer name,
|
||||
identified by the :attr:`base_layer` class attribute.
|
||||
|
||||
``OpenLayersWidget``
|
||||
|
||||
.. class:: OpenLayersWidget
|
||||
|
||||
This is the default widget used by all GeoDjango form fields.
|
||||
``template_name`` is ``gis/openlayers.html``.
|
||||
This is the default widget used by all GeoDjango form fields. Attributes
|
||||
are:
|
||||
|
||||
.. attribute:: base_layer
|
||||
|
||||
.. versionadded:: 6.0
|
||||
|
||||
``nasaWorldview``
|
||||
|
||||
.. attribute:: template_name
|
||||
|
||||
``gis/openlayers.html``.
|
||||
|
||||
.. attribute:: map_srid
|
||||
|
||||
``3857``
|
||||
|
||||
``OpenLayersWidget`` and :class:`OSMWidget` use the ``ol.js`` file hosted
|
||||
on the ``cdn.jsdelivr.net`` content-delivery network. You can subclass
|
||||
@ -157,12 +179,14 @@ Widget classes
|
||||
|
||||
.. class:: OSMWidget
|
||||
|
||||
This widget uses an OpenStreetMap base layer to display geographic objects
|
||||
on. Attributes are:
|
||||
This widget specialized :class:`OpenLayersWidget` and uses an OpenStreetMap
|
||||
base layer to display geographic objects on. Attributes are:
|
||||
|
||||
.. attribute:: template_name
|
||||
.. attribute:: base_layer
|
||||
|
||||
``gis/openlayers-osm.html``
|
||||
.. versionadded:: 6.0
|
||||
|
||||
``osm``
|
||||
|
||||
.. attribute:: default_lat
|
||||
.. attribute:: default_lon
|
||||
@ -179,3 +203,37 @@ Widget classes
|
||||
tiles.
|
||||
|
||||
.. _FAQ answer: https://help.openstreetmap.org/questions/10920/how-to-embed-a-map-in-my-https-site
|
||||
|
||||
.. versionchanged:: 6.0
|
||||
|
||||
The ``OSMWidget`` no longer uses a custom template. Consequently, the
|
||||
``gis/openlayers-osm.html`` template was removed.
|
||||
|
||||
.. _geometry-widgets-customization:
|
||||
|
||||
Customizing the base layer used in OpenLayers-based widgets
|
||||
-----------------------------------------------------------
|
||||
|
||||
.. versionadded:: 6.0
|
||||
|
||||
To customize the base layer displayed in OpenLayers-based geometry widgets,
|
||||
define a new layer builder in a custom JavaScript file. For example:
|
||||
|
||||
.. code-block:: javascript
|
||||
:caption: ``path-to-file.js``
|
||||
|
||||
MapWidget.layerBuilder.custom_layer_name = function () {
|
||||
// Return an OpenLayers layer instance.
|
||||
return new ol.layer.Tile({source: new ol.source.<ChosenSource>()});
|
||||
};
|
||||
|
||||
Then, subclass a standard geometry widget and set the ``base_layer``::
|
||||
|
||||
from django.contrib.gis.forms.widgets import OpenLayersWidget
|
||||
|
||||
|
||||
class YourCustomWidget(OpenLayersWidget):
|
||||
base_layer = "custom_layer_name"
|
||||
|
||||
class Media:
|
||||
js = ["path-to-file.js"]
|
||||
|
@ -73,6 +73,9 @@ Minor features
|
||||
function rotates a geometry by a specified angle around the origin or a
|
||||
specified point.
|
||||
|
||||
* The new :attr:`.BaseGeometryWidget.base_layer` attribute allows specifying a
|
||||
JavaScript map base layer, enabling customization of map tile providers.
|
||||
|
||||
:mod:`django.contrib.messages`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@ -332,6 +335,11 @@ Miscellaneous
|
||||
refactored to use Python's :py:class:`email.message.Message` for parsing.
|
||||
Input headers exceeding 10000 characters will now raise :exc:`ValueError`.
|
||||
|
||||
* Widgets from :mod:`django.contrib.gis.forms.widgets` now render without
|
||||
inline JavaScript in templates. If you have customized any geometry widgets
|
||||
or their templates, you may need to :ref:`update them
|
||||
<geometry-widgets-customization>` to match the new layout.
|
||||
|
||||
.. _deprecated-features-6.0:
|
||||
|
||||
Features deprecated in 6.0
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* global QUnit, MapWidget */
|
||||
/* global QUnit, MapWidget, ol */
|
||||
'use strict';
|
||||
|
||||
QUnit.module('gis.OLMapWidget');
|
||||
@ -91,3 +91,84 @@ QUnit.test('MapWidget.IsCollection', function(assert) {
|
||||
widget = new MapWidget(options);
|
||||
assert.ok(widget.options.is_collection);
|
||||
});
|
||||
|
||||
QUnit.test('MapWidget.layerBuilder.osm returns OSM layer', function(assert) {
|
||||
const layer = MapWidget.layerBuilder.osm();
|
||||
assert.ok(layer instanceof ol.layer.Tile, 'Layer is Tile');
|
||||
assert.ok(layer.getSource() instanceof ol.source.OSM, 'Source is OSM');
|
||||
});
|
||||
|
||||
QUnit.test('MapWidget.layerBuilder.nasaWorldview returns XYZ layer', function(assert) {
|
||||
const layer = MapWidget.layerBuilder.nasaWorldview();
|
||||
assert.ok(layer instanceof ol.layer.Tile, 'Layer is Tile');
|
||||
assert.ok(layer.getSource() instanceof ol.source.XYZ, 'Source is XYZ');
|
||||
assert.ok(layer.getSource().getUrls()[0].includes('earthdata.nasa.gov'), 'URL is NASA-hosted');
|
||||
});
|
||||
|
||||
QUnit.test('MapWidget uses default OSM base layer when none specified', function(assert) {
|
||||
const widget = new MapWidget({
|
||||
id: 'id_point',
|
||||
map_id: 'id_point_map',
|
||||
geom_name: 'Point'
|
||||
});
|
||||
assert.ok(widget.baseLayer.getSource() instanceof ol.source.OSM, 'Default base layer is OSM');
|
||||
});
|
||||
|
||||
QUnit.test('MapWidget uses named base layer from layerBuilder', function(assert) {
|
||||
const widget = new MapWidget({
|
||||
id: 'id_point',
|
||||
map_id: 'id_point_map',
|
||||
geom_name: 'Point',
|
||||
base_layer: 'nasaWorldview'
|
||||
});
|
||||
assert.ok(widget.baseLayer.getSource() instanceof ol.source.XYZ, 'Uses named base layer from builder');
|
||||
});
|
||||
|
||||
QUnit.test('MapWidget uses passed-in base layer object directly', function(assert) {
|
||||
const customLayer = new ol.layer.Tile({source: new ol.source.OSM()});
|
||||
const widget = new MapWidget({
|
||||
id: 'id_point',
|
||||
map_id: 'id_point_map',
|
||||
geom_name: 'Point',
|
||||
base_layer: customLayer
|
||||
});
|
||||
assert.strictEqual(widget.baseLayer, customLayer, 'Uses provided layer object');
|
||||
});
|
||||
|
||||
QUnit.test('initMapWidgetInSection initializes widgets and skips __prefix__', function(assert) {
|
||||
const wrapper1 = document.createElement('div');
|
||||
wrapper1.className = 'dj_map_wrapper';
|
||||
wrapper1.id = 'id_point_map_wrapper';
|
||||
wrapper1.innerHTML = `
|
||||
<textarea id="id_point"></textarea>
|
||||
<div class="dj_map" id="id_point_map"></div>
|
||||
<script type="application/json" id="mapwidget-options">
|
||||
{ "geom_name": "Point" }
|
||||
</script>
|
||||
`;
|
||||
document.body.appendChild(wrapper1);
|
||||
|
||||
const wrapper2 = document.createElement('div');
|
||||
wrapper2.className = 'dj_map_wrapper';
|
||||
wrapper2.id = 'form-__prefix__-map_wrapper';
|
||||
wrapper2.innerHTML = `
|
||||
<textarea id="id_fake"></textarea>
|
||||
<div class="dj_map" id="id_fake_map"></div>
|
||||
<script type="application/json" id="mapwidget-options">
|
||||
{ "geom_name": "MultiPoint" }
|
||||
</script>
|
||||
`;
|
||||
|
||||
document.body.appendChild(wrapper2);
|
||||
|
||||
const maps = window.initMapWidgetInSection(document);
|
||||
|
||||
assert.equal(maps.length, 1, 'Only one map widget is initialized');
|
||||
assert.ok(maps[0] instanceof MapWidget, 'Map is instance of MapWidget');
|
||||
assert.equal(maps[0].options.id, 'id_point', 'Correct widget was initialized');
|
||||
assert.equal(maps[0].options.map_id, 'id_point_map', 'Map ID is correct');
|
||||
|
||||
// Clean up
|
||||
wrapper1.remove();
|
||||
wrapper2.remove();
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ from django.contrib.gis import forms
|
||||
from django.contrib.gis.forms import BaseGeometryWidget, OpenLayersWidget
|
||||
from django.contrib.gis.geos import GEOSGeometry
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.template.defaultfilters import json_script
|
||||
from django.test import SimpleTestCase, override_settings
|
||||
from django.utils.html import escape
|
||||
|
||||
@ -183,6 +184,37 @@ class GeometryFieldTest(SimpleTestCase):
|
||||
"unrecognized as WKT EWKT, and HEXEWKB.)",
|
||||
)
|
||||
|
||||
def test_override_attrs(self):
|
||||
self.assertIsNone(forms.BaseGeometryWidget.base_layer)
|
||||
self.assertEqual(forms.BaseGeometryWidget.geom_type, "GEOMETRY")
|
||||
self.assertEqual(forms.BaseGeometryWidget.map_srid, 4326)
|
||||
self.assertIs(forms.BaseGeometryWidget.display_raw, False)
|
||||
|
||||
class PointForm(forms.Form):
|
||||
p = forms.PointField(
|
||||
widget=forms.OpenLayersWidget(
|
||||
attrs={
|
||||
"base_layer": "some-test-file",
|
||||
"map_srid": 1234,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
form = PointForm()
|
||||
rendered = form.as_p()
|
||||
|
||||
attrs = {
|
||||
"base_layer": "some-test-file",
|
||||
"geom_type": "POINT",
|
||||
"map_srid": 1234,
|
||||
"display_raw": False,
|
||||
"required": True,
|
||||
"id": "id_p",
|
||||
"geom_name": "Point",
|
||||
}
|
||||
expected = json_script(attrs, "mapwidget-options")
|
||||
self.assertInHTML(expected, rendered)
|
||||
|
||||
|
||||
class SpecializedFieldTest(SimpleTestCase):
|
||||
def setUp(self):
|
||||
@ -250,15 +282,29 @@ class SpecializedFieldTest(SimpleTestCase):
|
||||
),
|
||||
}
|
||||
|
||||
def assertMapWidget(self, form_instance):
|
||||
def assertMapWidget(self, form_instance, geom_name):
|
||||
"""
|
||||
Make sure the MapWidget js is passed in the form media and a MapWidget
|
||||
is actually created
|
||||
"""
|
||||
self.assertTrue(form_instance.is_valid())
|
||||
rendered = form_instance.as_p()
|
||||
self.assertIn("new MapWidget(options);", rendered)
|
||||
self.assertIn("map_srid: 3857,", rendered)
|
||||
|
||||
map_fields = [
|
||||
f for f in form_instance if isinstance(f.field, forms.GeometryField)
|
||||
]
|
||||
for map_field in map_fields:
|
||||
attrs = {
|
||||
"base_layer": "nasaWorldview",
|
||||
"geom_type": map_field.field.geom_type,
|
||||
"map_srid": 3857,
|
||||
"display_raw": False,
|
||||
"required": True,
|
||||
"id": map_field.id_for_label,
|
||||
"geom_name": geom_name,
|
||||
}
|
||||
expected = json_script(attrs, "mapwidget-options")
|
||||
self.assertInHTML(expected, rendered)
|
||||
self.assertIn("gis/js/OLMapWidget.js", str(form_instance.media))
|
||||
|
||||
def assertTextarea(self, geom, rendered):
|
||||
@ -279,7 +325,7 @@ class SpecializedFieldTest(SimpleTestCase):
|
||||
geom = self.geometries["point"]
|
||||
form = PointForm(data={"p": geom})
|
||||
self.assertTextarea(geom, form.as_p())
|
||||
self.assertMapWidget(form)
|
||||
self.assertMapWidget(form, "Point")
|
||||
self.assertFalse(PointForm().is_valid())
|
||||
invalid = PointForm(data={"p": "some invalid geom"})
|
||||
self.assertFalse(invalid.is_valid())
|
||||
@ -295,7 +341,7 @@ class SpecializedFieldTest(SimpleTestCase):
|
||||
geom = self.geometries["multipoint"]
|
||||
form = PointForm(data={"p": geom})
|
||||
self.assertTextarea(geom, form.as_p())
|
||||
self.assertMapWidget(form)
|
||||
self.assertMapWidget(form, "MultiPoint")
|
||||
self.assertFalse(PointForm().is_valid())
|
||||
|
||||
for invalid in [
|
||||
@ -310,7 +356,7 @@ class SpecializedFieldTest(SimpleTestCase):
|
||||
geom = self.geometries["linestring"]
|
||||
form = LineStringForm(data={"f": geom})
|
||||
self.assertTextarea(geom, form.as_p())
|
||||
self.assertMapWidget(form)
|
||||
self.assertMapWidget(form, "LineString")
|
||||
self.assertFalse(LineStringForm().is_valid())
|
||||
|
||||
for invalid in [
|
||||
@ -325,7 +371,7 @@ class SpecializedFieldTest(SimpleTestCase):
|
||||
geom = self.geometries["multilinestring"]
|
||||
form = LineStringForm(data={"f": geom})
|
||||
self.assertTextarea(geom, form.as_p())
|
||||
self.assertMapWidget(form)
|
||||
self.assertMapWidget(form, "MultiLineString")
|
||||
self.assertFalse(LineStringForm().is_valid())
|
||||
|
||||
for invalid in [
|
||||
@ -340,7 +386,7 @@ class SpecializedFieldTest(SimpleTestCase):
|
||||
geom = self.geometries["polygon"]
|
||||
form = PolygonForm(data={"p": geom})
|
||||
self.assertTextarea(geom, form.as_p())
|
||||
self.assertMapWidget(form)
|
||||
self.assertMapWidget(form, "Polygon")
|
||||
self.assertFalse(PolygonForm().is_valid())
|
||||
|
||||
for invalid in [
|
||||
@ -355,7 +401,7 @@ class SpecializedFieldTest(SimpleTestCase):
|
||||
geom = self.geometries["multipolygon"]
|
||||
form = PolygonForm(data={"p": geom})
|
||||
self.assertTextarea(geom, form.as_p())
|
||||
self.assertMapWidget(form)
|
||||
self.assertMapWidget(form, "MultiPolygon")
|
||||
self.assertFalse(PolygonForm().is_valid())
|
||||
|
||||
for invalid in [
|
||||
@ -370,7 +416,7 @@ class SpecializedFieldTest(SimpleTestCase):
|
||||
geom = self.geometries["geometrycollection"]
|
||||
form = GeometryForm(data={"g": geom})
|
||||
self.assertTextarea(geom, form.as_p())
|
||||
self.assertMapWidget(form)
|
||||
self.assertMapWidget(form, "GeometryCollection")
|
||||
self.assertFalse(GeometryForm().is_valid())
|
||||
|
||||
for invalid in [
|
||||
@ -393,8 +439,8 @@ class OSMWidgetTest(SimpleTestCase):
|
||||
form = PointForm(data={"p": geom})
|
||||
rendered = form.as_p()
|
||||
|
||||
self.assertIn("ol.source.OSM()", rendered)
|
||||
self.assertIn("id: 'id_p',", rendered)
|
||||
self.assertIn('"base_layer": "osm"', rendered)
|
||||
self.assertIn('<textarea id="id_p"', rendered)
|
||||
|
||||
def test_default_lat_lon(self):
|
||||
self.assertEqual(forms.OSMWidget.default_lon, 5)
|
||||
@ -415,9 +461,20 @@ class OSMWidgetTest(SimpleTestCase):
|
||||
form = PointForm()
|
||||
rendered = form.as_p()
|
||||
|
||||
self.assertIn("options['default_lon'] = 20;", rendered)
|
||||
self.assertIn("options['default_lat'] = 30;", rendered)
|
||||
self.assertIn("options['default_zoom'] = 17;", rendered)
|
||||
attrs = {
|
||||
"base_layer": "osm",
|
||||
"geom_type": "POINT",
|
||||
"map_srid": 3857,
|
||||
"display_raw": False,
|
||||
"default_lon": 20,
|
||||
"default_lat": 30,
|
||||
"default_zoom": 17,
|
||||
"required": True,
|
||||
"id": "id_p",
|
||||
"geom_name": "Point",
|
||||
}
|
||||
expected = json_script(attrs, "mapwidget-options")
|
||||
self.assertInHTML(expected, rendered)
|
||||
|
||||
|
||||
class GeometryWidgetTests(SimpleTestCase):
|
||||
@ -425,15 +482,15 @@ class GeometryWidgetTests(SimpleTestCase):
|
||||
# The Widget.get_context() attrs argument overrides self.attrs.
|
||||
widget = BaseGeometryWidget(attrs={"geom_type": "POINT"})
|
||||
context = widget.get_context("point", None, attrs={"geom_type": "POINT2"})
|
||||
self.assertEqual(context["geom_type"], "POINT2")
|
||||
self.assertEqual(context["widget"]["attrs"]["geom_type"], "POINT2")
|
||||
# Widget.get_context() returns expected name for geom_type.
|
||||
widget = BaseGeometryWidget(attrs={"geom_type": "POLYGON"})
|
||||
context = widget.get_context("polygon", None, None)
|
||||
self.assertEqual(context["geom_type"], "Polygon")
|
||||
self.assertEqual(context["widget"]["attrs"]["geom_name"], "Polygon")
|
||||
# Widget.get_context() returns 'Geometry' instead of 'Unknown'.
|
||||
widget = BaseGeometryWidget(attrs={"geom_type": "GEOMETRY"})
|
||||
context = widget.get_context("geometry", None, None)
|
||||
self.assertEqual(context["geom_type"], "Geometry")
|
||||
self.assertEqual(context["widget"]["attrs"]["geom_name"], "Geometry")
|
||||
|
||||
def test_subwidgets(self):
|
||||
widget = forms.BaseGeometryWidget()
|
||||
@ -443,7 +500,10 @@ class GeometryWidgetTests(SimpleTestCase):
|
||||
{
|
||||
"is_hidden": False,
|
||||
"attrs": {
|
||||
"base_layer": None,
|
||||
"display_raw": False,
|
||||
"map_srid": 4326,
|
||||
"geom_name": "Geometry",
|
||||
"geom_type": "GEOMETRY",
|
||||
"display_raw": False,
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user