Quickstart
Make these changes to the project settings file:
INSTALLED_APPS = [
# Add these at the bottom:
'django.contrib.sites',
'django.contrib.sitemaps',
]
# Define SITE_ID:
SITE_ID = 1
Add a field called modified
and a method called get_absolute_url()
to a model:
class Post(models.Model):
# Add "modified" field:
modified = models.DateTimeField(auto_now=True)
# Add "get_absolute_url()" method:
def get_absolute_url(self):
return reverse('blog:detail',
args=[self.slug])
Make these changes to your URL configuration file:
from django.contrib import admin
# Import these
from django.urls import include, path
from blog.models import Post
from django.contrib.sitemaps.views import sitemap
from django.contrib.sitemaps import GenericSitemap
from mysite.sitemaps import StaticViewSitemap
# Add `sitemaps` dictionary:
sitemaps = {
'blog': GenericSitemap({
'queryset': Post.objects.all(),
'date_field': 'modified',
}, priority=0.9),
'static': StaticViewSitemap,
}
urlpatterns = [
# ...
# Pass the `sitemaps` dictionary to the `sitemap` view:
path('sitemap.xml', sitemap,
{'sitemaps': sitemaps},
name='django.contrib.sitemaps.views.sitemap'),
]
Create a file called sitemaps.py
somewhere (in the mysite
directory in this case) and add these lines to it:
from django.contrib.sitemaps import Sitemap
from django.urls import reverse
class StaticViewSitemap(Sitemap):
def items(self):
return ['about']
def location(self, item):
return reverse(item)
Visit /sitemap.xml
:
Full tutorial
Sitemap is an XML file that lists URLs for a site. Each URL can be associated with metadata. For example, we can tell the crawler how important a URL is related to other URLs in the site, how often it usually changes and when it was last updated. This helps search engines to index your site.
Setup
Run these commands to setup a project:
mkdir mysite && cd mysite
python3 -m venv venv
source venv/bin/activate
pip install django
django-admin startproject mysite .
python manage.py startapp blog
python manage.py startapp pages
Configure settings.py
Add these lines to the project settings.py
file:
INSTALLED_APPS = [
# START
'blog.apps.BlogConfig',
'pages.apps.PagesConfig',
# END
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# START
'django.contrib.sites',
'django.contrib.sitemaps',
# END
]
# HERE
SITE_ID = 1
TEMPLATES = [
{
# ...
# HERE
'DIRS': [os.path.join(BASE_DIR, 'templates')],
# ...
},
]
django.contrib.sites
enables the "sites" framework. The sitemap framework needs it to work properly. It gets the domain for the URLs using the currentSite
object.django.contrib.sitemaps
enables the sitemap framework.- We wire our apps into the project by adding
blog.apps.BlogConfig
andpages.apps.PagesConfig
to theINSTALLED_APPS
list. SITE_ID = 1
associates aSite
object from the database (with an ID of1
) to this settings file.'DIRS': [os.path.join(BASE_DIR, 'templates')]
makes Django look for template files in the roottemplates
directory.
Add Post model
Add a class called Post
to the blog app models.py
file:
from django.db import models
from django.utils.text import slugify
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=255,
unique=True)
slug = models.SlugField(max_length=50,
unique=True,
default='')
# HERE
modified = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super().save(*args, **kwargs)
# HERE
def get_absolute_url(self):
return reverse('blog:detail',
args=[self.slug])
- The
modified
field is used for the sitemaplastmod
attribute. It tells when the item was last modified. - The
get_absolute_url()
method is used for the sitemaploc
attribute. This is the page URL.
An example from the generated sitemap:
<loc>http://example.com/blog/my-post-title/</loc>
<lastmod>2020-05-30</lastmod>
Run migrations
python manage.py makemigrations && \
python manage.py migrate
This makes the necessary changes to the database.
Create posts
python manage.py shell
>>> from blog.models import Post
>>> import uuid
>>> for x in range(10):
... post = Post.objects.create(title='%d' % uuid.uuid4())
...
>>>
python manage.py shell
starts the Python interactive interpreter.for x in range(10)
loops 10 times.- uuid4 is used to create a random title for each object. The title has to be unique because we are using it to create a slug for the object. That slug goes to the generated sitemap through the
get_absolute_url
method.
Update URLs
Edit mysite/urls.py
and add these lines to it:
from django.contrib import admin
from django.urls import include, path
from blog.models import Post
from django.contrib.sitemaps.views import sitemap
from django.contrib.sitemaps import GenericSitemap
from sitemaps.sitemaps import StaticViewSitemap
from pages.views import PostListView
from django.views.generic import TemplateView
sitemaps = {
'blog': GenericSitemap({
'queryset': Post.objects.all(),
'date_field': 'modified',
}, priority=0.9),
'static': StaticViewSitemap,
}
urlpatterns = [
path('', PostListView.as_view(), name='home'),
path('blog/', include('blog.urls')),
path('about/', TemplateView.as_view(template_name='about.html'), name='about'),
path('sitemap.xml', sitemap,
{'sitemaps': sitemaps},
name='django.contrib.sitemaps.views.sitemap'),
path('admin/', admin.site.urls),
]
- The
sitemaps
dictionary holds information about the items we want to add to the sitemap. In this example it holds two labels.blog
maps to aGenericSitemap
instance that adds post URLs to the sitemap.static
adds static pages (anabout
page in this case) using a custom sitemap class calledStaticViewSitemap
. The labels can map toSitemap
class instances (GenericSitemap(..)
) orSitemap
classes (StaticViewSitemap
). - The
GenericSitemap
convenience class allows you to create basic sitemaps by passing it aqueryset
entry.'queryset': Post.objects.all()
means that all posts will be added to the generated sitemap.'date_field': 'modified'
adds alastmod
attribute to each item using thePost
modelmodified
field. Thedate_field
entry is optional.
Additionally, you can add the following keyword arguments:
priority
informs the crawler how important an item is relative to other items in your site. Search engines may use this information when crawling your site.changefreq
specifies how often the content is likely to change. For example:changefreq="weekly"
. Search engines may use this information when crawling your site.protocol
defines the protocol for the sitemap URLs (http
orhttps
). By default, the request protocol is used, orhttp
if the sitemap is generated without a request.
Finally we pass in the sitemaps
dictionary to the sitemap
view: {'sitemaps': sitemaps}
. This generates the sitemap when a client accesses /sitemap.xml
. Note: the sitemap is generated dynamically. A file called sitemap.xml
is not added to your codebase.
Add StaticViewSitemap
Create a file called sitemaps.py
in the project package directory and add these lines to it:
from django.contrib.sitemaps import Sitemap
from django.urls import reverse
class StaticViewSitemap(Sitemap):
def items(self):
return ['about']
def location(self, item):
return reverse(item)
- Use the
items
method to return the pages you want to add to the sitemap. You should return a QuerySet or sequence. In this case we provide theabout
URL pattern name in a list. - The list items that we return from the
items
method are passed to thelocation
method. For static pages we can't use the modelget_absolute_url()
method so we use thelocation
method to return the URL for each item in the list. Thereverse
function gets the URL using the URL pattern name (about
).
Add blog URL configuration file
Create a file called urls.py
in the blog app directory:
from django.urls import path
from .views import PostDetailView
app_name = 'blog'
urlpatterns = [
path('<slug:slug>/', PostDetailView.as_view(), name='detail')
]
Edit blog app views
Edit the blog app views.py
file and add the following lines to it:
from django.shortcuts import render
from django.views.generic.detail import DetailView
from .models import Post
class PostDetailView(DetailView):
model = Post
template_name = 'blog/detail.html'
context_object_name = 'post'
This allows us to get information about one post and display it on the detail page.
Add PostListView
Edit the pages app views.py
file and add the following lines to it:
from django.shortcuts import render
from django.views.generic.list import ListView
from blog.models import Post
class PostListView(ListView):
model = Post
template_name = 'pages/home.html'
context_object_name = 'posts'
This allows us to list all posts on the homepage.
Update home template
Create a directory called templates
in the project root. Create a directory called pages
inside it. Add a file called home.html
to the templates/pages
directory:
<h1>Home</h1>
<ul>
{% for post in posts %}
<li>
<a href="{{ post.get_absolute_url }}">{{ post }}</a>
</li>
{% endfor %}
</ul>
This displays post links on the homepage.
Add about.html
Add a file called about.html
in the templates/pages
directory:
<h1>About</h1>
Add detail.html
Create a directory called blog
in the templates
directory. Add a file called detail.html
in it:
<h1>{{ post.title }}</h1>
This displays the post title.
Result
Run the development server:
python manage.py runserver
Visit /sitemap.xml
and you should see the generated sitemap:
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://example.com/blog/84513265289461491745040709504185504735/</loc>
<lastmod>2020-05-30</lastmod>
<priority>0.9</priority>
</url>
<url>
<loc>http://example.com/about/</loc>
</url>
</urlset>