How to Use Leaflet with Django

Django map tutorial

Hey there! Some links on this page may be affiliate links which means that, if you choose to make a purchase, I may earn a small commission at no extra cost to you. I greatly appreciate your support!

In this Django tutorial, you will learn how to use Leaflet JS and GeoJson to display a map on an HTML page for every country. While this tutorial is specific to GeoDjango, the fundamentals of mapping with leafletjs are covered.

This blog post is a continuation from How to Use GeoDjango and accompanies the following video tutorial.

YouTube video

1. Default Sort Order

We can specify the default sort order to be alphabetic in our Country model definition at world/models.py. Add the following Meta class.

...
class Country(models.Model):
    ...
    class Meta:
        ordering = ['name']

2. Display a List of Countries

Make a folder at world/templates and then a new file world/templates/index.html with the following content that will display a list of countries.

<html>  
<body>
    <h1>Hello world</h1>
    {% for country in countries %}
    <p>{{ country.name }}</p>
    {% endfor %}
</body> 
</html>

We need to define the countries variable and pass it into our template. Edit world/views.py.

from django.shortcuts import render
from world.models import Country

def index(request):
    countries = Country.objects.all()
    context = {'countries': countries}
    return render(request, 'index.html', context)

Now we can tell Django about our new web page in world/urls.py.

from django.contrib import admin
from django.urls import path
from world import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('home/', views.index),
]

Run the development server and you will see a list of countries.

The start of an alphabetical list of countries displayed on a web page

 

2. Create a Page for Each Country

Let’s link each country to a country-specific page.

Tell Django about our country-specific web pages in world/urls.py.

from django.contrib import admin
from django.urls import path
from world import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('home/', views.index),
    path('home/<str:sov_a3>', views.country),
]

The <str:sov_a3> syntax simply means that when someone goes to the page for Afghanistan at http://localhost/home/AFG for example, the string “AFG” will be passed as an argument with the variable name sov_a3 to the country function in our world/views.py file which we will create next.

from django.shortcuts import render
from world.models import Country

def index(request):
    countries = Country.objects.all()
    context = {'countries': countries}
    return render(request, 'index.html', context)

def country(request, sov_a3):
    country = Country.objects.get(sov_a3=sov_a3)
    context = {'country': country}
    return render(request, 'country.html', context)

Notice how we do very similar things in the country and index functions. The difference is that we are returning all countries in the index function and returning only the country that matches the sov_a3 field in the country function.

Now we can create the actual HTML page for the countries with another Django template at world/templates/country.html.

<html>
<body>
    <h1>{{ country.name }}</h1>
</body>
</html>

Finally let’s link to the country pages with the sov_a3 code in world/templates/index.html.

<html>  
<body>
    <h1>Hello world</h1>  
    {% for country in countries %}
    <p><a href="{{ country.sov_a3 }}">{{ country.name }}<a></p>
    {% endfor %}
</body> 
</html>

Testing this out turns the list of countries into hyperlinks.

The start of an alphabetical list of countries as hyperlinks

Clicking on Afghanistan for example will show the page for Afghanistan like this.

Simple HTML page displaying the country of Afghanistan

3. Display a Map for Each Country

We will use the following Python packages.

Install these packages with the pip package manager.

sudo apt update
sudo apt install python3-pip
pip install django-geojson django-leaflet

Add these to your Django apps list at gis/settings.py.

...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.gis',
    'world',
    'leaflet',
    'djgeojson',
]
...

Replace everything in world/templates/country.html with the following.

{% load geojson_tags %}
{% load leaflet_tags %}
<html>
<header>
{% leaflet_js %}
{% leaflet_css %}
</header>
<body>
    <h1>{{ country.name }}</h1>
    <div>{% leaflet_map "map" callback="map_init" %}</div>
    <script type="text/javascript">
    function map_init(map, options) {
      var area = L.geoJson({{ country.geom|geojsonfeature|safe }}).addTo(map);
      map.fitBounds(area.getBounds());
    }
  </script>
</body>
</html>

I will explain the above HTML. The first couple lines load the Django packages that we just installed (geojson_tags and leaflet_tags). In the header, we link to the Leaflet library with leaflet_js and leaflet_css.  In the body, we define a div with a callback to the map_init function. The map_init JavaScript function is defined between the script tag on the next line,  and this function takes the geom field from the Django country object that was passed in and converts it to a GeoJSON object called area which is added to the map. Finally, the fitBounds function uses the bounds of the map data to zoom into the country.

Testing this out for any country will display the map of that country as defined by the Natural Earth shapefile from the previous tutorial.

HTML page displaying the country of Afghanistan map

Facebook
Twitter
Pinterest
LinkedIn
Reddit

Meet Tony

Tony from Tony Teaches Tech headshot

With a strong software engineering background, Tony is determined to demystify the web. Discover why Tony quit his job to pursue this mission. You can join the Tony Teaches Tech community here.

Leave a Reply

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