Improving the Admin

The Django admin saves us mountains of time. Back when Django still wasn’t quite Django and it was an internal project it was common for the team to leave a meeting, scribble a schema on a napkin and have the admin ready for data entry in the matter of a couple hours.

Models are still my first stop for any project – build the basic frame, and start gathering data with the admin. But over the years we’ve all come to expect more from applications and Django has gotten much better tools for it too. Need to reorder fields? Hide the stuff that rarely gets entered? Use a custom form? It’s in the docs. With the GeoDjango admin though, things aren’t as well documented. So let’s look at some of the simplest things that can make an application better.

Changing the default map

Two modes are available, admin.GeoModelAdmin and admin.OSMGeoAdmin.  GeoModelAdmin uses very simple tiles from OpenLayers that have state lines, rivers / water sources and a few other unmarked areas. I can’t imagine how it would be useful because there’s nothing to reference from, but if you have your own set of WMS tiles you can set wms_url and it will use them.

By default when you load the admin you’ll find yourself looking in the Atlantic off the coast of Africa. If you’re US-centric, try

class CampgroundAdmin(admin.GeoModelAdmin):
    default_lon = -98
    default_lat = 38.5
    default_zoom = 3

Which will center you in a spot where you can see the entire US. default_zoom of 4 is a closer view if you don’t need to see New England. For Kansas, default_zoom of 6 gets you the whole state. Experiment with these three values and you can set up your web app to be “optimized” for entry anywhere. As even a neogeographer can tell, they’re in decimal degrees like you get from your GPS.

This works in OSMGeoAdmin as well (the admin I use the most). Just as it says on the tin, OSMGeoAdmin uses OpenStreetMap so there’s a LOT of data available on the basemap you edit on top of. BUT this piece of the docs means one thing – “that uses a spherical mercator projection” – we can’t just put decimal degrees in and it work. But we have the tools to make it work.

>>> from django.contrib.gis.geos import Point
>>> center = Point((-98, 38.5), srid=4326)
>>> center.transform(900913)
>>> center.y
4650301.836738959
>>> center.x
-10909310.097740812

We put these coordinates on the admin object, and get a street map in the admin for editing.

class CampgroundOSMAdmin(admin.OSMGeoAdmin):
    default_lon = -10909310
    default_lat = 4650301
    default_zoom = 6

Other options in the admin class are available but some of the ones I could see using don’t seem to work – like max_zoom / min_zoom. “modifiable” set to false will force geometries not to be editable, but that’s not particularly useful in the admin.

Adding More Layers

We can add additional WMS layers to our maps, too – in this example we’ll add the current NEXRAD radar composite and a Topographical map. (additional layer info can be found at http://trac.osgeo.org/openlayers/wiki/AvailableWMSServices but many of them did not work for me)

Add the map_template attribute to our admin class:

class CampgroundAdmin(admin.GeoModelAdmin):
    default_lon = -98
    default_lat = 38.5
    default_zoom = 6
    max_zoom = 18
    min_zoom = 6
    map_template = 'gis/admin/openlayers_extralayers.html'

Now we need to create the template, and the javascript that will initialize the additional layers.

In gis/admin/openlayers_extralayers.html we change which file is included:

{% extends "openlayers.html" %}

{% block openlayers %}{% include "gis/admin/openlayers_extralayers.js" %}{% endblock %}

And in openlayers_extralayers.js we define the new layers:

{% extends "gis/admin/openlayers.js" %}

{% block extra_layers %}
    topo_layer = new OpenLayers.Layer.WMS( "USA Topo", "http://terraservice.net/ogcmap.ashx", {layers: 'DRG'} );
    {{ module }}.map.addLayer(topo_layer);
    nexrad_layer = new OpenLayers.Layer.WMS( "NEXRAD", "http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi?", {layers:"nexrad-n0r",transparent:"true",format:'image/png'} );
    {{ module }}.map.addLayer(nexrad_layer);
{% endblock extra_layers %}

IF you want this to be the default behavior for ALL of your admins, just name the file openlayers.html and openlayers.js instead of openlayers_extralayers.html/js. NOTE: If you do this you must copy/paste the entirety of openlayers.html and openlayers.js into YOUR openlayers.html and openlayers.js and put your code inside of the appropriate blocks. If you use my example, the template will extend itself, cause an infinite recursion and the dev server or your server process will promptly quit with no warning. Ask me how I know!! 🙂

You can download openlayers_extralayers.html and openlayers_extralayers.js for use in your own projects.

Note this won’t work with OSMGeoAdmin immediately unless you use the openlayers.html / openlayers.js naming and follow the above concerns. If you don’t want to do that (and I would try to avoid it because it could get you stuck on an old version of the javascript in the future) create your own osm.html:

{% extends "gis/admin/openlayers_extralayers.html" %}
{% block openlayers %}{% include "gis/admin/osm_extralayers.js" %}{% endblock %}

and osm_extralayers.js:

{% extends "gis/admin/openlayers_extralayers.js" %}
{% block base_layer %}new OpenLayers.Layer.OSM.Mapnik("OpenStreetMap (Mapnik)");{% endblock %}

Since we named it osm.html it will take over for all OpenStreetMap admins. If that’s not what you want, name it something else and override map_template on the admin class.

Another note: the Topo layer used doesn’t understand the OSM projection so it won’t work in OSMGeoAdmin.

Leave a Reply

Your email address will not be published. Required fields are marked *