Djangoのmodelにおいてslugfieldを使うメリット


Djangoはいいぞ!

いいぞ!ということです。
Djangoにおけるslugがどういうものかについては触れられている記事はあったのですが、じゃあDjangoでslug機能を使うべき理由って何よ?書き方ってどうなのよ?という点に触れた記事はないようなので書こうと思いました。

slugとはなんぞや?

結論からいうとslugを使う理由はSEO対策のためなのですが、最初にまずはslugについての説明をしましょう。

Django Documentの中の用語集においてslugは以下のような説明をされています。

スラグ (slug)
ある要素に対して付けられた短いラベルで、英語のアルファベット、数字、アンダーバー、ハイフンからなり、ふつうは URL に使われる。例示すると、次のようなよくあるブログエントリの URL では:
https://www.djangoproject.com/weblog/2008/apr/12/spring/
最後にある文字列 (spring) がスラグです。
用語集

特定の文言をURLの末尾に追加する、という認識でいいかと思います。

じゃあDjangoでslugを使うメリットって何よ?

Googleの公式ガイドラインの一つに以下のようなタイトル、そして文言があるように、
GoogleはシンプルなURLの方がいいよ!それもアドレスからページの内容が予想できるようなやつね!とURLに一定の規則を持つことを推奨しています。

サイトの URL 構造はできる限りシンプルにします。論理的かつ人間が理解できる方法で(可能な場合は ID ではなく意味のある単語を使用して)URL を構成できるよう、コンテンツを分類します。たとえば、航空機(aviation)に関する情報を探している場合、http://en.wikipedia.org/wiki/Aviation のような URL であれば、見ただけで必要な情報であるかどうかを判断できます。http://www.example.com/index.php?id_sezione=360&sid=3a5ebc944f
シンプルな URL 構造を維持する

これ以外にもSEO対策のガイドラインはあるのですが(各ブラウザに対応しているかどうか、HTTPSを実装しているかなど)、一つの要素としてシンプルかつ何のページを見ているか一目瞭然なURLにすることはSEO対策をする上で大事な要素となっています。

つまり、
https://www.djangoproject.com/articles/1
よりも、
https://www.djangoproject.com/articles/1/introduction
のような形の方が望ましいと言えます。

Djangoでブログを構築する際には記事のidを末尾にするのではなく、slugを設けることによって記事のに内容に関する記述として使うことができます。
これがDjangoには最初から組み込まれているので、使う方がSEO対策的には良いと言えます。

Djangoでのslugの書き方は?

例えば、記事についてのmodelを書くときは以下のように書きます。

models.py
from django.db import models
from django.template.defaultfilters import slugify # new
from django.urls import reverse

class Article(models.Model):
    title = models.CharField(max_length=255)
    body = models.TextField()
    slug = models.SlugField(null=False, unique=True)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('article_detail', kwargs={'slug': self.slug})

    def save(self, *args, **kwargs): # new
        if not self.slug:
            self.slug = slugify(self.title)
        return super().save(*args, **kwargs)

CharFieldと同じように、max_length を指定することもでき、max_lengthが指定されていないとき、max_lengthは50文字になります。
暗黙的にField.db_indexをTrueにセットします。

views.py
from django.views.generic import ListView, DetailView

from .models import Article

class ArticleListView(ListView):
    model = Article
    template_name = 'article_list.html'


class ArticleDetailView(DetailView):
    model = Article
    template_name = 'article_detail.html'
urls.py
from django.urls import path

from .views import ArticleListView, ArticleDetailView

urlpatterns = [
    path('<slug:slug>', ArticleDetailView.as_view(), name='article_detail'), # new
    path('', ArticleListView.as_view(), name='article_list'),
]
admin.py
from django.contrib import admin

from .models import Article

class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title', 'body',)
    prepopulated_fields = {'slug': ('title',)} # new

admin.site.register(Article, ArticleAdmin)
article_list.html
<h1>Articles</h1>
{% for article in object_list %}
  <ul>
    <li><a href="{{ article.get_absolute_url }}">{{ article.title }}</a></li>
  </ul>
{% endfor %}
article_detail.html
<div>
  <h2>{{ object.title }</h2>
  <p>{{ object.body }}</p>
</div>