Django Sitemap Tutorial


Why do we need sitemap?

Suppose you have build a website that has 23 pages. Now you know that it has that many page, but how will a search engine know that? If you want your website to be indexed properly on search engines, you will need to provide a sitemap (map of your website). Your website will be indexed even if you don't submit a proper sitemap, but using a sitemap file makes the process fast and much more efficient.

What do sitemaps look like?

A sitemap contains links to all the pages on a website that you want search engines to crawl. Search engines read this file to more intelligently crawl your site. Sitemap tells how often a page is updated and how important ti is in relation to other pages on the website.

The image below shows an example of sitemap.

Sitemap could exist at a single location like learndjango.xyz/sitemap.xml. But if your site has more than 50,000 urls, you will need a sitemap index, that links multiple sitemap files.

Django comes with built-in sitemap framework to automate this task.

Story App

Before we generate a sitemap, we need a working Django website! So, let's create a Story Writing app for this purpose. This tutorial is mainly focused on sitemaps, so I will not go too deep to explain every step, I will just give commands mostly. If you find some part confusing you can consult other tutorials to learn Django basics.

On the terminal, install Django, create new project called mysite, and a new app called story.


$ cd ~
$ mkdir stories && cd stories
$ pipenv install django==3.0.3 #creating python virtual environment
$ pipenv shell #activating virtual environment
(stories) $ django-admin startproject mysite . #creating mysite project in current directory
(stories) $ python manage.py startapp stories #creating stories app
            
Then in the mysite/settings.py file add our stories app.

# mysite/settings.py
INSTALLED_APPS = [
  'django.contrib.admin',
  'django.contrib.auth',
  'django.contrib.contenttypes',
  'django.contrib.sessions',
  'django.contrib.messages',
  'django.contrib.staticfiles',
  'stories', # new app
]
            
Run the migrate command to initialize the database.

(stories) $ python manage.py migrate # ./manage.py migrate could also work
            

Creating Model

Our model will contain a title and content for each story. We can also add str and get_absolute_url methods as well.


# stories/models.py
from django.db import models
from django.urls import reverse


class Story(models.Model):
  title = models.CharField(max_length=250)
  content = models.TextField()

  def __str__(self):
    return self.title

  def get_absolute_url(self):
    return reverse('story_detail', args=[str(self.id)])
            
Now run makemigrations and migrate to reflect the changes in database.

(stories) $ python manage.py makemigrations stories
(stories) $ python manage.py migrate
            

Admin

The Django admin is a convenient way to play around with our data so create a superuser account to log in.


(stories) $ python manage.py createsuperuser #do what it says next
            
Now before we can use the stories app in our admin site, we need to update stories/admin.py to display our new app in admin.

# stories/admin.py
from django.contrib import admin
from .models import Story

class StoryAdmin(admin.ModelAdmin):
  list_display = ('title', 'content')

admin.site.register(Story, StoryAdmin)
            
Start the local server with the command python manage.py runserver and log in at http://127.0.0.1:8000/admin with your superuser account.

Now add some Stories to your website. Use +Add button.

Well, now we have some content. Next we set our URL paths, views and templates.

URL paths

We need to update two URL files: edit the top-level mysite/urls.py and create urls.py file in stories app.


(stories) $ touch stories/urls.py
            

# mysite/urls.py
from django.contrib import admin
from django.urls import path, include # new

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('stories.urls')), # new
]
            

# stories/urls.py
from django.urls import path

from .views import StoryListView, StoryDetailView

urlpatterns = [
    path('', StoryDetailView.as_view(), name='story_detail'),
    path('', StoryListView.as_view(), name='story_list'),
]
            

Views

We will use the built-in Generic Class-Based Views for list and detail pages.


# stories/views.py
from django.views.generic import ListView, DetailView

from .models import Story

class StoryListView(ListView):
    model = Story
    template_name = 'story_list.html'

class StoryDetailView(DetailView):
    model = Story
    template_name = 'story_detail.html'
          

Templates

Finally, lets create the required templates.


(stories) $ mkdir stories/templates
(stories) $ touch stories/templates/story_list.html
(stories) $ touch stories/templates/story_detail.html
            
The recipe_list.html page displays a list of all recipes.

<!-- stories/story_list.html -->
<h1>Stories</h1>
{% for story in object_list %}
  <ul>
  <li><a href="{{ story.get_absolute_url }}">{{ story.title }}</a></li>
  </ul>
{% endfor %}
            
And story_detail.html displays an individual story.

<!-- story_detail.html -->
<div>
  <h2>{{ object.title }}</h2>
  <p>{{ object.content }}</p>
</div>
            

You might need to add 'templates' in stories/settings.py in Templates, as 'DIRS': ['templates'],

That's it! We now have 3 pages in our app: - homepage with all stories - a detail page for each of our stories in the database

Now our app is ready and we can write code for sitemaps! First we need add 'django.contrib.sitemaps' to INSTALLED_APPS in mysite/settings.py


# mysite/settings.py
INSTALLED_APPS = [ 
  'django.contrib.admin',
  'django.contrib.auth',
  'django.contrib.contenttypes',
  'django.contrib.sessions',
  'django.contrib.messages',
  'django.contrib.staticfiles',
  'django.contrib.sites', # new
  'django.contrib.sitemaps', # new

  'stories',
]

SITE_ID = 1 # new
            
The sites framework requires an update to our database so need to run migrate.
(stories) $ python manage.py migrate

The simplest approach to implement sitemaps in to use GenericSitemaps. Now lets add it to our mysite/urls.py file.


from django.contrib import admin
from django.urls import path, include
from django.contrib.sitemaps import GenericSitemap # new
from django.contrib.sitemaps.views import sitemap # new

from stories.models import Story

info_dict = {
    'queryset': Story.objects.all(),
}

urlpatterns = [
    path('', include('stories.urls')),
    path('admin/', admin.site.urls),
    path('sitemap.xml', sitemap, # new
        {'sitemaps': {'blog': GenericSitemap(info_dict, priority=0.6)}},
        name='django.contrib.sitemaps.views.sitemap'),
    ]  
            
Make sure the server is running and visit http://127.0.0.1:8000/sitemap.xml.

That is the sitemap we wanted! It using example.com as the name of our site. Thats because of sites framework. We can change that in Django admin. We can also change priority and changefreq. The current version of sitemap uses http, but if our production site user https than that will be reflected in sitemap too. We can also change the protocol.