Django ブログサイト sitemaps.py サイトマップ生成
2020/07/04 (更新:2020/11/19)
Django フレームワークの機能を使用し、本サイトのサイトマップを生成します。
投稿者毎のトップページとブログ記事ページを出力します。
システム設定に、Django のアプリケーションとサイトの設定を追加します。
settings
INSTALLED_APPS = [
...
'django.contrib.sites',
'django.contrib.sitemaps',
]
SITE_ID = 1
共通クラスです。
Sitemapを継承し実装します。
get_urlsをオーバーライドし、多言語対応のhreflang要素を追加します。
BlogSitemap
class BlogSitemap(Sitemap):
def reverse(self, obj):
pass
def location(self, obj):
path = self.reverse(obj)
if obj['language_code'] == settings.LANGUAGE_CODE:
return path
else:
return f'/{obj["language_code"]}{path}'
def get_urls(self, page=1, site=None, protocol=None):
urls = super().get_urls(page, site, protocol)
# alternate
kwalternates_w = {}
for url in urls:
obj = url['item']
alternates = kwalternates_w.get(obj['key'], [])
kwalternates_w[obj['key']] = alternates
alternates.append({
'lang_code': obj['language_code'],
'location': url['location'],
})
# alternate x-default
kwalternates = {}
for key, alternates in kwalternates_w.items():
if len(alternates) < 2:
continue
alternate_default = next((a for a in alternates if a['lang_code'] == 'en'), None)
if not alternate_default:
alternate_default = next((a for a in alternates if a['lang_code'] == settings.LANGUAGE_CODE), None)
alternates.append({
'lang_code': 'x-default',
'location': alternate_default['location'],
})
kwalternates[key] = alternates
for url in urls:
obj = url['item']
alternates = kwalternates.get(obj['key'])
if alternates:
url['alternates'] = alternates
return urls
多言語対応のhreflangタグを出力するため、Django のテンプレートを置き換えます。
sitemap.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
{% spaceless %}
{% for url in urlset %}
<url>
<loc>{{ url.location }}</loc>
{% if url.lastmod %}<lastmod>{{ url.lastmod|date:"Y-m-d" }}</lastmod>{% endif %}
{% if url.changefreq %}<changefreq>{{ url.changefreq }}</changefreq>{% endif %}
{% if url.priority %}<priority>{{ url.priority }}</priority>{% endif %}
{% for alternate in url.alternates %}
<xhtml:link rel="alternate" hreflang="{{ alternate.lang_code }}" href="{{ alternate.location }}"/>
{% endfor %}
</url>
{% endfor %}
{% endspaceless %}
</urlset>
投稿者毎のトップページを出力するクラスです。
共通クラスBlogSitemapを継承し実装します。
投稿記事テーブルを投稿者毎に集約し最終の更新日時を出力します。
AuthorSitemap
class AuthorSitemap(BlogSitemap):
changefreq = "always"
priority = 0.5
def items(self):
items = models.PostContent.objects.values_list('post__author__user__username', 'language_code') \
.annotate(updated_date=Max('post__updated_date')).order_by('post__author_id')
return [{'key':item[0], 'language_code':item[1], 'updated_date':item[2]} for item in items]
def reverse(self, obj):
return reverse('blog:index', args=(obj['key'],))
def lastmod(self, obj):
return obj['updated_date']
投稿記事ページを出力するクラスです。
共通クラスSitemapを継承し実装します。
PostSitemap
class PostSitemap(BlogSitemap):
changefreq = "never"
priority = 1.0
def items(self):
items = models.PostContent.objects.values_list('post__id', 'post__author__user__username', 'language_code') \
.annotate(updated_date=Max('post__updated_date')).order_by('post__author_id')
return [{
'key': f'{item[0]},{item[1]}',
'id': item[0],
'auth_name': item[1],
'language_code': item[2],
'updated_date': item[3],
} for item in items]
def reverse(self, obj):
return reverse('blog:detail', args=(obj['auth_name'], obj['id'],))
def lastmod(self, obj):
return obj['updated_date']