はじめに
いつも心は虹色に! nikkieです。
2018/11/10に参加した #モグモグDjango で、Django Girls Tutorial Extensions がDjango2系で動作するように確認しました。
ExtensionsをDjango 2系で動かす中で気づいたことをアウトプットします。
勉強会の概要
akiyokoさんの『現場で使えるDjangoの教科書』頒布をきっかけに始まった、Djangoのもくもく会です。
第3回 モグモグDjango - connpass
Django のもくもく会 モグモグDjango を開催します. Django初心者の方から,上級者の方までどなたでもご参加下さい(^^)
この会は,しーーっんとしているもくもく会ではなく,質問や分からないところがあったら,その場で聞くことができる環境にしたい!と思っています.
取り組んだこと
Django Gitls Tutorial(Djangoでブログアプリを作るチュートリアル)にはExtensionsという拡張版があります。
Introduction · Django Girls Tutorial: Extensions
9月のPyConJP Sprintにて有志で翻訳したのですが、ExtensionsはコードがDjango 1系ということが発覚していました。1
GitHub - DjangoGirlsJapan/tutorial-extensions: Additional tasks for tutorial (翻訳はWIP、また未レビューです)
Extensions翻訳を完了させるために、コードをDjango 2系に置き換えたいとメンバー間で考えており、今回のもくもく会で着手しました。
達成したこと
Django 2系に書き換える対象は、3ページあります。
- Homework: add more to your website! · Django Girls Tutorial: Extensions (機能追加)
- Homework: secure your website · Django Girls Tutorial: Extensions (セキュリティ)
- Homework: create comment model · Django Girls Tutorial: Extensions (コメント機能)
このうち、機能追加とセキュリティについてDjango2系で動作確認できました。
#モグモグdjango
— nikkie (@ftnext) 2018年11月10日
ログインしたユーザがブログ記事を作成・編集できるようになりました!
url関数をpath関数に置き換えただけで済み、Django2系対応は思った以上に少なく済みました。https://t.co/XWCDTwreji pic.twitter.com/4Jt1kIH30B
以下では、機能追加のパートを進める中で気づいたことを書いていきます。(セキュリティについては記事を分けます)
ブログアプリに機能追加
このパートで追加する機能は、大きく分けて3つです。
- ドラフト(下書き)の一覧機能
- ドラフトを公開する機能
- 記事を削除する機能
前提
以下の環境で動かしています。
なお、機能追加のチュートリアルは、9月から11月までの間にコードがDjango2系に更新されていました。(確認しやすかったです。ありがとうございます!)
Djangoの機能追加手順
これまで手を動かしてきた経験を踏まえると、機能追加するには以下の順がよさそうです。2
- 新規ページへ遷移するリンクを追加
- 新規ページのURL設定
- 新規ページのビュー作成
- 新規ページのテンプレート作成
Djangoは機能追加する際に触る箇所が多いので、「機能追加手順のチェックリストを持つと、使いやすく感じられるかな」と考えています。
ドラフトの一覧機能
まず、ドラフト一覧画面に遷移するアイコンをヘッダーに追加します。
(記事が作成できるのはログインしているユーザのみとしていたので、ドラフトが見えるのもログインしているユーザに限定しています)
blog/templates/blog/base.html
<!-- 省略 --> <div class="page-header"> {% if user.is_authenticated %} <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a> <a href="{% url 'post_draft_list' %}" class="top-menu"><span class="glyphicon glyphicon-edit"></span></a> <!-- 追加 --> {% endif %} <h1><a href="/">Django Girls Blog</a></h1> </div> <!-- 省略 -->
次に、ドラフト一覧画面のURLを追加します。
blog/urls.py
urlpatterns = [ # 省略 path('drafts/', views.post_draft_list, name='post_draft_list'), # 追加 ]
ビューの編集では2つのことを行います。
1つ目は、記事を作成・編集したときにpublished_date
を設定しないようにすること。
これにより、作成した記事が即時公開されなくなり、ドラフト扱いとすることができます。
- 新規作成した場合、published_dateは空(ドラフト状態)
- ドラフトの記事を編集した場合、published_dateは空のまま(ドラフトのまま)
- 既に公開した記事を編集した場合、published_dateの値は変わらない(公開されたまま)
2つ目は、ドラフトの記事の一覧を表示するための関数を追加すること。
ドラフトの記事はpublished_date
がnull
です。
blog/views.py
def post_new(request): if request.method == "POST": form = PostForm(request.POST) if form.is_valid(): post = form.save(commit=False) post.author = request.user # post.published_date = timezone.now() # 削除 post.save() return redirect('post_detail', pk=post.pk) else: form = PostForm() return render(request, 'blog/post_edit.html', {'form': form}) def post_edit(request, pk): post = get_object_or_404(Post, pk=pk) if request.method == "POST": form = PostForm(request.POST, instance=post) if form.is_valid(): post = form.save(commit=False) post.author = request.user # post.published_date = timezone.now() # 削除 post.save() return redirect('post_detail', pk=post.pk) else: form = PostForm(instance=post) return render(request, 'blog/post_edit.html', {'form': form}) # 以下を追加 def post_draft_list(request): posts = Post.objects.filter(published_date__isnull=True).order_by('created_date') return render(request, 'blog/post_draft_list.html', {'posts': posts})
最後に、ドラフト一覧のテンプレートを作成します。
blog/templates/blog/post_draft_list.html
{% extends 'blog/base.html' %} {% block content %} {% for post in posts %} <div class="post"> <p class="date">created: {{ post.created_date|date:'d-m-Y' }}</p> <h1><a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a></h1> <p>{{ post.text|truncatechars:200 }}</p> </div> {% endfor %} {% endblock %}
このテンプレートには2つのフィルタが登場します。
- date
- 指定したフォーマットで日付を表示するフィルタ
- d-m-Y:日(2桁)-月(2桁)-年(4桁)
- truncatechar
- 文字列の後ろを切り捨てて、指定した長さ(ここでは200文字)とするフィルタ
- 切り捨てた文字列は...と3文字で表されるので、
post.text
の先頭から197文字が取り出される - ドキュメント記載の例
{{ "Joel is a slug"|truncatechars:9 }}
→Joel i...
(Joel i
の6文字+...の3文字で合計9文字)
ここまでで、ドラフトボタンからドラフト一覧画面に遷移できます。
ドラフトを公開する機能
まず、記事の詳細画面にドラフトを公開するボタンを作ります。
(公開されていない記事=ドラフトの場合のみ、公開ボタンが見えるようにしています3)
blog/templates/blog/post_detail.html
{% if post.published_date %} <div class="date"> {{ post.published_date }} </div> {% else %} <!-- 追加 --> <a class="btn btn-default" href="{% url 'post_publish' pk=post.pk %}">Publish</a> <!-- 追加 --> {% endif %}
次に、ドラフト公開に使うURLを追加します。
Extensionsの原文のまま('post/<pk>/publish/'
)でもビューのget_object_or_404
は動きましたが、チュートリアル本編のURLの設定に合わせて'post/<int:pk>/publish/'
としています。4
blog/urls.py
urlpatterns = [ # 省略 path('drafts/', views.post_draft_list, name='post_draft_list'), path('post/<int:pk>/publish/', views.post_publish, name='post_publish'), # 追加 ]
最後にビューにpost_publish
関数を追加します。
blog/views.py
def post_publish(request, pk): post = get_object_or_404(Post, pk=pk) post.publish() return redirect('post_detail', pk=pk)
追加した関数で使われているpublish
メソッドですが、これは Djangoモデル · Django Girls Tutorial で定義したものです。
Extensionsにて、伏線がようやく回収されました!
記事を公開した後は記事詳細にリダイレクトするので、新規で作成するテンプレートはありません。
以上で、ドラフト公開ボタンが付きました!
記事を削除する機能
記事の削除機能の追加は、ドラフト公開機能と同様の流れです。
記事の削除ボタンが見えるのは、ログインしているユーザのみです。
blog/templates/blog/post_detail.html
{% if user.is_authenticated %} <a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a> <a class="btn btn-default" href="{% url 'post_remove' pk=post.pk %}"><span class="glyphicon glyphicon-remove"></span></a> <!-- 追加 --> {% endif %}
削除機能の場合も、URLの設定はチュートリアル本編の記載に合わせています。
blog/urls.py
urlpatterns = [ # 省略 path('drafts/', views.post_draft_list, name='post_draft_list'), path('post/<int:pk>/publish/', views.post_publish, name='post_publish'), path('post/<int:pk>/remove/', views.post_remove, name='post_remove'), # 追加 ]
blog/views.py
def post_remove(request, pk): post = get_object_or_404(Post, pk=pk) post.delete() return redirect('post_list')
delete
はDjangoモデルが持っているメソッドだそうです。
こうして、記事が削除できるようになりました!
感想
Django Girls Tutorialの懇切丁寧な解説から方向転換し、Extensionsは「気になるところは自分で調べてね」という感じです。(独り立ちを意識しているのかもしれません)
ファイルの変更箇所を明確にする意図でこの記事は詳しく書いていますが、実際に手を入れる箇所はそんなに多くはありませんでした。
下書き機能もついて、ブログアプリの機能が充実してきて嬉しい限りです^ ^
教えていただいたもの
- 11/10近辺のQiitaのトレンド:「今後必要になるプログラム言語」にひたすらマサカリを飛ばす - Qiita
- Kindleストア : 【60%OFF】技術書典シリーズ特集 (11/29まで)
- 次回は読書会 第1回 モグモグDjango読書会 - connpass
- akiyokoさんによるDjangoアンケート
「初学者・初心者向け Django の学習ロードマップ(2018年版)」というブログ記事を執筆中。
— akiyoko / 3日目東U16a@C95 /「現場で使えるDjangoの教科書」販売中 (@aki_yok) 2018年11月21日
そのインプットのひとつとして、Djangoに関するアンケートへの回答を募集しています。ちょっとDjangoをかじったことのある程度の方もぜひご協力を。〆切は11/28頃を予定https://t.co/oYeSLTB3RA#Django
今回アウトプットが遅くなってしまいましたが、モグモグDjango参加者、運営者の皆さま、どうもありがとうございました!
Extensionsはperfectじゃなくてもいいので、なる早でdoneに持っていく所存です。
脚注
-
path
関数ではなくurl
関数が使われていたため、発覚しました。↩ -
参照 週末ログ | Django Girls Tutorial翻訳レビュー 100%到達! - nikkie-ftnextの日記 なお、機能追加にモデルが必要な場合は作成が必要です。(ビューの前に作ることになると思います)↩
-
ブログを書いていて気づいたのですが、Extensionsのとおりに進めると、ログインしていないユーザでも公開できてしまうみたいですね。セキュリティのパートを終えると、ログインしているユーザだけが公開できるようになりました↩
-
原文のまま(
'post/<pk>/publish/'
)だとstrのpk(例:'1')でget_object_or_404
が動きます。'post/<int:pk>/publish/'
とすると、intのpk(例:1)がget_object_or_404
に渡ります。↩