In this tutorial, you will learn how to import a shapefile into a PostgreSQL database and display map data in the Django admin console.
The system I used was Ubuntu 22.10 on DigitalOcean. Here’s $200 free DigitalOcean credits if you’d like to follow along.
This blog post accompanies the following video tutorial.
1. Install PostgreSQL
First, update system packages and install the PostGIS package which includes PostgreSQL among other necessary dependencies.
sudo apt update sudo apt upgrade sudo apt install postgis
2. Setup PostgreSQL Database
Let’s switch to the postgres user and create a database called gitdb. Then login to the Postgres console so we can issue database commands.
sudo su - postgres createdb gisdb psql gisdb
Create a database user called george and password with appropriate privileges and settings.
CREATE EXTENSION postgis; CREATE USER george WITH PASSWORD 'geographyisfun'; ALTER ROLE george SET client_encoding TO 'utf8'; ALTER ROLE george SET default_transaction_isolation TO 'read committed'; ALTER ROLE george SET timezone TO 'UTC'; GRANT ALL PRIVILEGES ON DATABASE gisdb TO george; exit
3. Create a New User
Rather than using the root user, let’s create another user.
exit adduser tony usermod -aG sudo tony su - tony
4. Install Python
Install Django, Python, and other necessary packages so Python can communicate with the PostgreSQL database.
sudo apt install python3-django python3-psycopg2
5. Setup Django
Start a new Django project called gis and a new Django app called world.
django-admin startproject gis cd gis python3 manage.py startapp world
Point Django to the PostgreSQL database. Edit the file at world/settings.py
.
... DATABASES = { 'default': { 'ENGINE': 'django.contrib.gis.db.backends.postgis', 'NAME': 'gisdb', 'USER': 'george', 'PASSWORD': 'geographyisfun', 'HOST': 'localhost', }, } ...
Also in world/settings.py
, add the django.contrib.gis app and world app to INSTALLED_APPS.
... INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.gis', 'world', ] ...
6. Get Shapefile from Natural Earth Data
We will be using a free shapefile from naturalearthdata.com that contains 209 sovereign countries at 1:10m scale.
In the world directory, create a maps subdirectory. Then use the wget command to download the zip file from the Natural Earth website.
cd world mkdir maps cd maps wget https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_0_sovereignty.zip
We need to install the unzip package first before unzipping the zip file.
sudo apt install unzip unzip ne_10m_admin_0_sovereignty.zip cd ../..
In the zip file, you’ll find a bunch of files including the shapefile that we’re interested in.
7. Import Shapefile to GeoDjango
We can use the ogrinspect command that is part of GeoDjango to easily define a Django model called Country to represent the shapefile data. The null option is necessary since some fields in the shapefile are empty.
python3 manage.py ogrinspect world/maps/ne_10m_admin_0_sovereignty.shp Country --mapping --multi --name-field name --null true
This command will print out the following two sections.
The first section is the Django model definition. Paste this section into world/models.py
replacing everything.
# This is an auto-generated Django model module created by ogrinspect. from django.contrib.gis.db import models class Country(models.Model): featurecla = models.CharField(max_length=19, null=True) ... geom = models.MultiPolygonField() def __str__(self): return self.name
The second section is a Python dictionary that maps the model fields to the PostgreSQL database.
# Auto-generated 'LayerMapping' dictionary for Country model country_mapping = { 'featurecla': 'featurcla', ... 'geom': 'MULTIPOLYGON', }
Create a new file called world/load.py
and paste the second section in there with the follow additional modifications.
from pathlib import Path from django.contrib.gis.utils import LayerMapping from .models import Country country_mapping = { 'featurecla': 'featurcla', ... 'geom': 'MULTIPOLYGON', } country_shp = Path(__file__).resolve().parent / 'maps' / 'ne_10m_admin_0_sovereignty.shp' def run(verbose=True): lm = LayerMapping(Country, country_shp, country_mapping, transform=False) lm.save(strict=True, verbose=verbose)
For reference, here is the Django documentation for LayerMapping.
Now let’s tell the Django database about our model definition.
python3 manage.py makemigrations python3 manage.py migrate
Then we can finally load the shapefile data into the database with the world/load.py
script.
python3 manage.py shell from world import load load.run()
8. Register Django Model in Admin Site
Django comes with a fantastic admin site that requires no coding. We simply need to register the Country model with the admin site and create a user to login to the site.
Modify world/admin.py
like this to register the Country model with the Django admin site.
from django.contrib.gis import admin from .models import Country admin.site.register(Country)
Create a Django user that can access the admin site.
python3 manage.py createsuperuser
9. View the Map Data in the Django Admin Site
Run the Django development server.
python3 manage.py runserver 0.0.0.0:8000
In a web browser, go to http://localhost:8000/admin (or http://YOUR_IP_ADDRESS:8000/admin if you’re on a remote server). Login with your Django user that you just created.
When you click on the “Countrys” link and pick any of the 209 countries in the list, you will see all the shapefile metadata including a map of the country like this.
Next, learn how to display map data on a web page with Leaflet and GeoJSON.