Создание Sitemap.xml для Django-проектов


Смею вас заверить, что ничего овер-сложного в этом нет и все описано и в официальной документации к Django Framework, правда написано он там как-то не особо доходчиво и есть несколько моментов которые доставляют.

Давайте рассмотрим пример файла sitemap.xml из википедии:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="https://www.sitemaps.org/schemas/sitemap/0.9">
   <url>
      <loc>http://example.com/</loc>
      <lastmod>2013-11-18</lastmod>
      <changefreq>monthly</changefreq>
      <priority>0.8</priority>
   </url>
</urlset>

В простейшем виде файл sitemap - это один файл содержащий набор <url>-записей для каждой страницы вашего сайта. У него есть основной атрибут <loc> - полный URL-страницы и необязательные параметры lastmod/changefreq/priority. Более подробно про каждый из параметров вы можете прочитать в той же википедии (https://ru.wikipedia.org/wiki/Sitemaps), а нас интересует практика и поэтому погнали.

Если вы следуете стандартной MVC-модели Django, то у вас наверняка есть несколько объектов которые описывают все типовые страницы вашего проекта и несколько страниц которые отдельно, поэтому лочично, что нам надо как-то передать информацию о SLUG и прочих атрибутах в Sitemap.xml.

Прежде всего импортируйте объект Sitemap из набора которых мы и будем строить Sitemap.xml для нашего сайта.

from django.contrib.sitemaps import Sitemap

Следующим этапом создаем класс потомок от этого объекта (вот рабочий пример):

class BlogPostSitemap(Sitemap):
  changefreq = "daily"
  priority = 0.9

  def items(self):
    return BlogPost.objects.filter(record_active=True, is_category = False)
   
  def location(self, obj):
    return '/blog/'+obj.slug+'/'

  def lastmod(self, obj):
    return obj.last_modify_date

Как вы видите функция items(self) возвращает отфильтрованный набор элементов которые будут использованы для построения этого блока sitemap.xml и именно эти элементы необходимо трансформировать в набор параметров для блока <url>. Как вы видите мы задали для блока blog статичные параметры для:

changefreq = "daily"
priority = 0.9

И теперь самое интересное. Мы создаем представления для элементов location и lastmod исходя из полей объектов переданных в items (наши отфильтрованные):

def location(self, obj):
   return '/blog/'+obj.slug+'/'

def lastmod(self, obj):
   return obj.last_modify_date

В итоге, получается примерно вот так.

Ок, думаю как из объекта сделать кусок файла Sitemap.xml вы теперь поняли, но у вас наверное появился вопрос, что делать со страницами для которых не предусмотрен собственный объект (разного рода статичные лэндинги и т.п.). В принципе ничего сложного и я например создаю массив статических страниц:

static_links = [{'url':'/','last_mod':datetime.datetime(2018, 8, 2, 0, 0)},
        {'url':'/skillz/','last_mod':datetime.datetime(2018, 8, 2, 0, 0)},
        {'url':'/blog/','last_mod':datetime.datetime(2018, 2, 8, 0, 0)},]

И просто передаю его как items все тому же объекту Sitemap:

class StaticSitemap(Sitemap):
  changefreq = "weekly"
  priority = 0.5

  def items(self):
    return static_links
   
  def location(self, obj):
    return obj['url']

  def lastmod(self, obj):
    return obj['last_mod']

Кажется, все круто и у нас есть пачка объектов каждый из которых описывает свой блок sitemap, но возникает вопрос, а как теперь сделать, чтобы склеенные объекты отображались при запросе http://<url-сайт>/sitemap.xml.

Во первых, в файл settings.py в раздел INSTALLED_APPS необходимо добавить приложение:

INSTALLED_APPS = [
  'django.contrib.auth',
  'django.contrib.contenttypes',
  'django.contrib.sessions',
  'django.contrib.messages',
  'django.contrib.staticfiles',
  'django.contrib.sitemaps',
  'admin',   
  ...

Во вторых, в файл urls.py мы импортируем все наши объекты унаследованные от Sitemap:

from blog.sitemap_models import BlogPostSitemap, StaticSitemap

В блок urlpatterns = [ добавляем соответственно запись:

path('sitemap.xml', sitemap, {'sitemaps': {'blog':BlogPostSitemap,'static':StaticSitemap,}}, name='django.contrib.sitemaps.views.sitemap'),

Как вы наверное догадались, мы передали наши объекты в виде пар ключ значение записью:

{'sitemaps': {'blog':BlogPostSitemap,'static':StaticSitemap,}},

В чистом виде этого вполне достаточно, а разного рода нюансы можно почитать в официальной документации.