Opened 3 years ago

Closed 3 years ago

#33049 closed Bug (duplicate)

TabularInline doesn't render properly MultiPolygonField in Django Admin

Reported by: Vladyslav Krylasov Owned by: nobody
Component: GIS Version: 2.2
Severity: Normal Keywords: gis, admin, MultiPolygonField, TabularInline
Cc: Vladyslav Krylasov Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: yes

Description (last modified by Vladyslav Krylasov)

I have a Django model that contains MultiPolygonField, so wanted to render it through the admin dashboard as an inline field.

models.py

class Geometry(models.Model):
  zone = models.ForeignKey(to=Zone, on_delete=models.CASCADE)
  mpoly = models.MultiPolygonField(null=True, blank=True)

admin.py

class GeometryInline(admin.TabularInline):
  model = Geometry
  max_num = 1

class ZoneAdmin(admin.GeoModelAdmin):
  model = Zone
  ...
  inlines = (GeometryInline,)

This renders such result:
https://i.ibb.co/njML4CP/Screenshot-from-2021-08-20-13-27-32.png

But when I make django.contrib.gis.admin.GeoModelAdmin as a mixin it works just fine.

admin.py

from django.contrib.gis import admin
from django.contrib.gis.admin.widgets import OpenLayersWidget
from django.contrib.gis.db import models
from django.contrib.gis.gdal import OGRGeomType
from django.forms import Media


class GeoAdminMixin:
    """
    Copied over from django.contrib.gis.admin.GeoModelAdmin

    The administration options class for Geographic models. Map settings
    may be overloaded from their defaults to create custom maps.
    """

    # The default map settings that may be overloaded -- still subject
    # to API changes.
    default_lon = 0
    default_lat = 0
    default_zoom = 4
    display_wkt = False
    display_srid = False
    extra_js = []
    num_zoom = 18
    max_zoom = False
    min_zoom = False
    units = False
    max_resolution = False
    max_extent = False
    modifiable = True
    mouse_position = True
    scale_text = True
    layerswitcher = True
    scrollable = True
    map_width = 600
    map_height = 400
    map_srid = 4326
    map_template = 'gis/admin/openlayers.html'
    openlayers_url = 'https://cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1/OpenLayers.js'
    point_zoom = num_zoom - 6
    wms_url = 'http://vmap0.tiles.osgeo.org/wms/vmap0'
    wms_layer = 'basic'
    wms_name = 'OpenLayers WMS'
    wms_options = {'format': 'image/jpeg'}
    debug = False
    widget = OpenLayersWidget

    @property
    def media(self):
        "Injects OpenLayers JavaScript into the admin."
        return super().media + Media(js=[self.openlayers_url] + self.extra_js)

    def formfield_for_dbfield(self, db_field, request, **kwargs):
        """
        Overloaded from ModelAdmin so that an OpenLayersWidget is used
        for viewing/editing 2D GeometryFields (OpenLayers 2 does not support
        3D editing).
        """
        if isinstance(db_field, models.GeometryField) and db_field.dim < 3:
            # Setting the widget with the newly defined widget.
            kwargs['widget'] = self.get_map_widget(db_field)
            return db_field.formfield(**kwargs)
        else:
            return super().formfield_for_dbfield(db_field, request, **kwargs)

    def get_map_widget(self, db_field):
        """
        Return a subclass of the OpenLayersWidget (or whatever was specified
        in the `widget` attribute) using the settings from the attributes set
        in this class.
        """
        is_collection = db_field.geom_type in (
            'MULTIPOINT',
            'MULTILINESTRING',
            'MULTIPOLYGON',
            'GEOMETRYCOLLECTION',
        )
        if is_collection:
            if db_field.geom_type == 'GEOMETRYCOLLECTION':
                collection_type = 'Any'
            else:
                collection_type = OGRGeomType(db_field.geom_type.replace('MULTI', ''))
        else:
            collection_type = 'None'

        class OLMap(self.widget):
            template_name = self.map_template
            geom_type = db_field.geom_type

            wms_options = ''
            if self.wms_options:
                wms_options = ["%s: '%s'" % pair for pair in self.wms_options.items()]
                wms_options = ', %s' % ', '.join(wms_options)

            params = {
                'default_lon': self.default_lon,
                'default_lat': self.default_lat,
                'default_zoom': self.default_zoom,
                'display_wkt': self.debug or self.display_wkt,
                'geom_type': OGRGeomType(db_field.geom_type),
                'field_name': db_field.name,
                'is_collection': is_collection,
                'scrollable': self.scrollable,
                'layerswitcher': self.layerswitcher,
                'collection_type': collection_type,
                'is_generic': db_field.geom_type == 'GEOMETRY',
                'is_linestring': db_field.geom_type in ('LINESTRING', 'MULTILINESTRING'),
                'is_polygon': db_field.geom_type in ('POLYGON', 'MULTIPOLYGON'),
                'is_point': db_field.geom_type in ('POINT', 'MULTIPOINT'),
                'num_zoom': self.num_zoom,
                'max_zoom': self.max_zoom,
                'min_zoom': self.min_zoom,
                'units': self.units,  # likely should get from object
                'max_resolution': self.max_resolution,
                'max_extent': self.max_extent,
                'modifiable': self.modifiable,
                'mouse_position': self.mouse_position,
                'scale_text': self.scale_text,
                'map_width': self.map_width,
                'map_height': self.map_height,
                'point_zoom': self.point_zoom,
                'srid': self.map_srid,
                'display_srid': self.display_srid,
                'wms_url': self.wms_url,
                'wms_layer': self.wms_layer,
                'wms_name': self.wms_name,
                'wms_options': wms_options,
                'debug': self.debug,
            }

        return OLMap



  class GeometryInline(GeoAdminMixin, admin.TabularInline):
    model = Geometry
    max_num = 1

Result:
https://i.ibb.co/ySbV2tV/Screenshot-from-2021-08-24-15-11-11.png

Am I missing something or Django needs a mixin for this situation?

Change History (3)

comment:1 by Vladyslav Krylasov, 3 years ago

Cc: Vladyslav Krylasov added
Needs documentation: set
Needs tests: set

comment:2 by Vladyslav Krylasov, 3 years ago

Description: modified (diff)

comment:3 by Mariusz Felisiak, 3 years ago

Needs documentation: unset
Needs tests: unset
Resolution: duplicate
Status: newclosed
Type: UncategorizedBug

Have you tried to use ModelAdmin instead of GeoModelAdmin? It seems to be a duplicate of #16417.

Moreover, we're going to deprecate GeoModelAdmin and OSMGeoAdmin (probably in Django 4.0), see #27674.

Note: See TracTickets for help on using tickets.
Back to Top