投稿日:2019年11月21日
Djangoは簡単にWebアプリケーションを作成できるフレームワークです。この記事は初心者の方向けのDjangoチュートリアルです。
このPartでは前回Modelに追加したuuidフィールドを使ってそれぞれのURLをidを使わない定義に直していきます。また新しくget_absolute_url()というメソッドも定義して使っていきます。
まずはviews.pyのarticle_detail()メソッドを改修しましょう。
[...]
def article_detail(request, uuid): # 受け取る引数名をわかりやすくuuidに変更
"""
記事の詳細を表示する
"""
article = get_object_or_404(Article, uuid=uuid) # 識別子をuuidにする
return render(
request,
"blog/article_detail.html",
{"article":article}
)
[...]
次にurls.pyのarticle_detailのURLのパターンを書き換えましょう。
from django.urls import path, re_path
from . import views
app_name = 'blog'
urlpatterns = [
# post views
path('', views.article_list, name='article_list'),
path('<uuid:uuid>/', # uuidをパースできる様に修正
views.article_detail,
name='article_detail'),
]
<int:id>の部分を<uuid:uuid>に変更しました。
※ちなみにDjango1.11までは正規表現で(?P<uuid>[0-9a-f-]+)の様に書かないと行けなかったんです。(便利になった!!)
次にtemplateの参照urlをuuidに変更しましょう。ここもidをuuidに変更するだけでOKです。
{% extends "base.html" %}
{% block title %}ブログ一覧|Sampleブログ{% endblock %}
{% block content %}
<h1>ブログ一覧</h1>
{% for article in articles %}
<h2>
<a href="{% url 'blog:article_detail' uuid=article.uuid %}">
{{ article.title }}
</a>
</h2>
<p class="date">
公開日:{{ article.publish }}
</p>
<p>by {{ article.author }}</p>
{{ article.body|truncatewords:30 }}
{% endfor %}
{% endblock %}
これでOKです。開発用サーバーを起動してhttp://localhost:8000/blog/にアクセスして動作を確認してみましょう。
$ python manage.py runserver
urlの識別子もuuidになったことで、それぞれの記事のURLが予測できなくなっていますね。
記事も問題なく見れるようになったことだし、冒頭に話したget_absolute_url()を実装して使ってみましょう。
DjangoではModelにはそのModelの詳細ページのURLを返すget_absolute_url()というメソッドを定義することが推奨されています。
なぜわざわざこのようなメソッドを定義するのか理由は後述するとして、まずは実装してみましょう。
[...]
from django.urls import reverse
[...]
class Article(models.Model):
[....]
def get_absolute_url(self):
return reverse("blog:article_detail", args=[self.uuid])
今現状ではブログ記事一覧からブログ記事詳細のページに飛ぶ時のリンクはDjangoの{% url %}タグを使っていますね。Articleクラスに実装したget_absolute_url()を使って置き換えてみましょう。
{% extends "base.html" %}
{% block title %}ブログ一覧|Sampleブログ{% endblock %}
{% block content %}
<h1>ブログ一覧</h1>
{% for article in articles %}
<h2>
<a href="{{ article.get_absolute_url }}">
{{ article.title }}
</a>
</h2>
<p class="date">
公開日:{{ article.publish }}
</p>
<p>by {{ article.author }}</p>
{{ article.body|truncatewords:30 }}
{% endfor %}
{% endblock %}
この様にget_absolute_url()はよくDjangoテンプレートに記述されます。Webサイトではレコード一覧にそのレコードの詳細へのリンクをつけるということを良くするのです。(ex.友達リストからその友達のHomeページ、商品リストからその商品の詳細ページ...)そのような場合にはこのメソッドを使うと良いでしょう。
get_absolute_url()はDjangoの管理サイトでもデフォルトで使われる様になっています。開発用サーバーを起動して管理サイトのArticleをみてみましょう。
$ python manage.py runserver
対象のモデルクラスにget_absolute_url()が実装してあると管理サイトから『VIEW ON SITE』で直接そのレコードの詳細ページに飛ぶことができます。
ブログサイトを運営していくことを考えると、管理サイトで記事を編集して、『VIEW ON SITE』から描画を確認して、気になるところがあればすぐ戻って編集...
の様になかなか便利に使えそうですね。
get_absolute_url()を使う理由は簡単に3つあります。
それぞれ解説します。
今回のPartではview関数がurlから受け取るパラメータをidからuuidに変更しました。実際にwebサイトを作っていく際にはこのような変更は何度も体験するでしょう。その時{% url %}タグでurlを定義していると、そのurlを毎回変更しなくてはいけません。
実際には同じurlの定義を同一ページ内で何度も使ったりする(最新記事一覧とか、関連記事一覧とか同じページにあるよね?)ため、変更のたびに大きな労力を使う事になるでしょう。
その点HTMLのurlもget_absolute_urlを使っておけば、urls.pyとmodelのget_absolute_url()関数の内部の処理の変更だけすればOKです。
HTMLはただでさえ複雑になりがちです。特にDjangoのテンプレート言語を使っていると余計ややこしくなるでしょう。Python側でまとめられる処理はそちらに書いてしまえるほうが確かに良さそうですね
例えば以下の様なtemplateがあったとしましょう。
<div>
{% for data in data_list %}
<a href="{{ data.get_absolute_url }}">data.name</a>
{% endfor %}
</div>
この時このテンプレートはフィールドにnameさえ持ってるどのModelでも使える事になります。もし{% url %}タグで書いてしまった場合URLの名前の参照と、パラメータの渡しがHTML上に書かれる事になるため、使い回しができないというわけです。
もちろんこのget_absolute_url()だけでなく、Djangoテンプレート上で同じ様な処理が複数回出てきた場合には、その処理をModel側で新たなメソッドとして管理できないか考えてみると、templateの管理が楽になってくるかも...?