【Django】ブログアプリを作ろう!
はじめに
この記事を書くきっかけは、Djangoの勉強を始めて、いざブログアプリでも作ってみようかな!としたらどうにもうまくいかなかったことにあります。
初学者なので間違っている部分、効率の悪い部分などあるかもしれませんがご了承ください。
勉強しながら、ブログアプリを作りながらこれを書いているので、おそらくなぞっていけば私と同じように進められるはずです。
要件
今回作りたいブログアプリのページ・機能は以下の通りです。
- 記事の一覧画面( / と /articles)
- ヘッダーの表示
- トップへのリンク
- 記事作成ページへ遷移できるリンク
- タイトル/本文/最終更新日時/作成日時を表示
- 記事は最終更新日時の新しいものから順に並べる
- 詳細画面へ遷移できるリンク
- ヘッダーの表示
- 記事の登録画面(/articles/create)
- 入力フォーム
- 全要素必須
- 文字数
- 投稿内容確認ボタン
- 入力フォーム
- 記事の登録内容確認画面(/articles/create_confirm)
- 入力内容の表示
- 文字数の表示
- 戻るボタン
- 投稿ボタン
- 記事の編集画面(/articles/{id}/edit)
- 編集フォーム
- 編集対象のレコードが初期値で入力されている
- バリデーション
- 全要素必須
- 文字数
- 投稿内容確認ボタン
- 戻るボタン
- 投稿ボタン
- 編集フォーム
- 記事の編集内容確認画面(/articles/{id}/edit_confirm)
- 入力内容の表示
- 戻るボタン
- 投稿ボタン
- 記事の詳細画面(/articles/{id})
- タイトル/本文/最終更新日時/作成日時を表示
- 編集画面へのリンク
- 削除確認画面へのリンク
- 記事の削除確認画面(/articles/{id}/delete_confirm)
- 削除対象のタイトル/本文/最終更新日時/作成日時を表示
- 戻るボタン
- 削除ボタンを押下で削除実行
- 404ページ
- 存在しないidをURL指定すると表示
投稿される記事は以下の条件とします。
- タイトル:max 50文字
- 記事本文:max 10,000文字
仮想環境
私はDockerでやっているので、他の環境をお使いの方は、適宜そちらに合わせて読み替えてください。
ディレクトリ構成
開発の下準備をしましょう。
最終的な構成は以下のようになるはずです。
Django
┣ articles
┃ ├ migrations
┃ │ ├ __init__.py
┃ │ └ 0001_initial.py
┃ ├ templates
┃ │ └ articles
┃ │ ├ base.html
┃ │ ├ articles_list.html
┃ │ ├ article_create.html
┃ │ ├ article_create_confirm.html
┃ │ ├ article_detail.html
┃ │ ├ article_edit.html
┃ │ ├ article_edit_confirm.html
┃ │ └ article_delete.html
┃ ├ __init__.py
┃ ├ admin.py
┃ ├ apps.py
┃ ├ forms.py
┃ ├ models.py
┃ ├ urls.py
┃ └ views.py
┣ blog
┃ ├ __init__.py
┃ ├ asgi.py
┃ ├ settings.py
┃ ├ urls.py
┃ └ wsgi.py
┣ manage.py
┗ requirements.txt
下準備として、以下のようなディレクトリ構成にしておきます。
Django
┣ articles
┃ ├ migrations
┃ ├ templates
┃ │ └ articles
┃ └ __init__.py
┣ blog
┃ ├ __init__.py
┃ ├ asgi.py
┃ ├ settings.py
┃ ├ urls.py
┃ └ wsgi.py
┣ manage.py
┗ requirements.txt
足りないファイルはここから作っていきましょう!
設定まわり
ライブラリのインストール
フォームにCSSなどを適用するために必要なライブラリをインストールしましょう!
ターミナルで以下を実行してください。
pip install django-widgets-improved
続いて、requirements.txt
に以下を追記してください。
django-widgets-improved==1.5.0
settings.py
まずsettings.py
を開きましょう。いろいろ書いてありますが、まずはINSTALLED_APPS
に追記します。
INSTALLED_APPS = [
'articles.apps.ArticlesConfig',
'widget_tweaks',
'django.contrib.sites',
・
・
・
]
これから作るアプリと、先ほどインストールしたライブラリ、管理者用サイトを記述してあげます。
次はテンプレートファイルの居場所を指示します。TEMPLATES
に追記します。
TEMPLATES = [
{
'DIRS': [BASE_DIR / 'articles' / 'templates'],
・
・
・
},
]
BASE_DIR
はmanage.py
のあるディレクトリを指しています。
apps.py
articles
ディレクトリにapps.py
を作成します。
from django.apps import AppConfig
class ArticlesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'articles'
この後作成するmodels.py
の内容をデータベースに反映するため、読み込む対象を記述します。
modelの定義
まずmodels.py
を作りましょう。
作成ディレクトリはarticles
です。
データベースの作成準備みたいなものだと理解しておいてください。
カラム
ブログアプリのために必要なカラムは以下としましょう。
カラム | 概要 | オプション |
---|---|---|
title | タイトル | 文字数制限:50 |
article_body | 本文 | 文字数制限:10,000 |
created_at | 作成日時 | 現在日時を入れる |
updated_at | 更新日時 | 現在日時を入れる |
models.py
上記のカラムを実現するために、models.py
は以下のように書きます。
from django.db import models
class Articles(models.Model):
title = models.CharField(verbose_name='タイトル', max_length=50)
article_body = models.TextField(verbose_name='本文', max_length=10000)
created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True)
updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True)
マイグレーション
以下のコマンドを実行して、models.py
に書いた内容を反映させましょう!
python3 manage.py makemigrations articles
python3 manage.py migrate
OKがたくさん出てきたら完了です!migrations
ディレクトリ内にいくつかファイルが生成されているはずです。models.py
は一旦閉じて大丈夫です。
ルーティング
blog/urls.py
URLの紐付けをしてあげましょう。
blog
ディレクトリ内にあるurls.py
を開きましょう。コメントアウトされている部分は邪魔なので削除しちゃいます。
必要なものをインポートします。以下を追記してください。
from django.urls import include
from articles.views import ArticlesListView
1行目は、すでにある
from django.urls import path
に追記する形で
from django.urls import path, include
としても良いでしょう。
2行目は、articles
ディレクトリ下にあるviews.py
からArticlesListView
クラスを読み込むよ、ということが書いてあります。
ArticlesListView
はまだ作っていないのでエラーを吐かれると思いますが、気にしなくて大丈夫です。
続いて、urlpatterns
に追記をします。以下のようになっていれば問題ありません。
urlpatterns = [
path('admin/', admin.site.urls),
path('articles/', include('articles.urls')),
path('', ArticlesListView.as_view(), name="home"),
]
2つ目のパスにあるarticles.urls
からurlを読み込みたいので、次はarticles
ディレクトリ内にurls.py
を作りましょう。
articles/urls.py
from django.urls import path
from . import views
app_name = "articles"
2つ目のインポートは、views.pyから全部読み込むよ、としています。
後々楽なので、app_name
と名前空間を設定しておきます。
さらに以下を追記しましょう。
urlpatterns = [
# 記事の一覧画面 /articles
path('', views.ArticlesListView.as_view(), name='articles_list'),
# 記事の登録画面 /articles/create
path('create/', views.ArticleCreateView.as_view(), name='article_create'),
# 記事の登録内容確認画面 /articles/create_confirm
path('create_confirm', views.ArticleCreateConfirm.as_view(), name='article_create_confirm'),
# 記事の詳細画面 /articles/{id}
path('<int:pk>/', views.ArticleDetailView.as_view(), name='article_detail'),
# 記事の編集画面 /articles/{id}/edit
path('<int:pk>/edit/', views.ArticleUpdateView.as_view(), name='article_edit'),
# 記事の編集内容確認画面 /articles/{id}/edit_confirm
path('<int:pk>/edit_confirm', views.ArticleUpdateConfirmView.as_view(), name='article_edit_confirm'),
# 記事の削除確認画面 /articles/{id}/delete_confirm
path('<int:pk>/delete_confirm', views.ArticleDeleteView.as_view(), name='article_delete'),
]
pk
はprimary key
の略です。よく使われる略語だそうです。
views.py
ルーティングでインポートしたいクラスたちを作りましょう。
まずviews.py
を作ります。作成ディレクトリはarticles
です。
記事の一覧画面
from django.views.generic.list import ListView
from .models import Articles
class ArticlesListView(ListView):
# 表示するページのhtmlの指定
template_name = 'articles/articles_list.html'
# modelの指定
model = Articles
# オブジェクト名の指定
context_object_name = 'articles_list'
# リストの表示順序の指定
ordering = ['-updated_at']
ListView
をインポートして、クラスの引数に入れてあげると、いい感じのリスト表示を実装してくれます。
template_name
で指定したhtmlは後ほど作ります。
model
は最初に作ったデータベースです。
記事の登録画面
views.py
に追記をしていきます。
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView
class ArticleCreateView(CreateView):
# 表示するページのhtmlの指定
template_name = 'articles/article_create.html'
# modelの指定
model = Articles
# form_classの指定
form_class = ArticleForm
# 登録成功時のアクション
success_url = reverse_lazy("articles:create_confirm")
ArticleForm
はこれから作成しますので、未定義のままで問題ありません。
forms.py
他のviewクラスを作る前に、formを作ってしまいましょう。
まずforms.py
を作ります。作成ディレクトリはarticles
です。
from django import forms
from .models import Articles
class ArticleForm(forms.ModelForm):
class Meta:
# 対象のモデルを指定
model = Articles
# 登録するカラムの指定
fields = ['title', 'article_body']
# ラベルを設定
labels = {
'title': 'タイトル',
'article_body': '記事本文',
}
# プレースホルダーを設定
widgets = {
'title': forms.TextInput(attrs={
'placeholder': '50字以内で入力してください'
}),
'article_body': forms.Textarea(attrs={
'placeholder': '1万字以内で入力してください',
'cols': '30',
'rows': '10',
}),
}
widgets
の設定は任意です。なくても問題ありません。
views.py (2回目)
views.py
に戻って、追記をしていきます。
記事の登録内容確認画面
from django.shortcuts import redirect, render
from .forms import ArticleForm
class ArticleCreateConfirm(View):
# HTTPの要求への応答を処理する。
def post(self, request, *args, **kwargs):
form = ArticleForm(request.POST or None)
# テンプレートに渡すのに必要
context = {'form': form}
if 'confirm' in request.POST:
return render(request, 'articles/article_create_confirm.html', context)
if 'back' in request.POST:
return render(request, 'articles/article_create.html', context)
if 'create' in request.POST:
# フォームのバリデーションを実行
if form.is_valid():
form.save()
return redirect('home')
else:
return render(request, 'articles/article_create.html', context)
記事の登録内容確認画面で、
- confirm = 確認 のPOSTがあれば、確認画面をレンダリングする
- back = 戻る のPOSTがあれば、記事登録画面に戻る
- create = 作成 のPOSTがあったときは、
- バリデーションの結果、正常値(True)なら、保存して、ホームにリダイレクトする
- バリデーションの結果、異常値(False)なら、記事登録画面に戻る
という分岐が設定されています。
記事の詳細画面
views.py
に追記をしていきます。
from django.views.generic.detail import DetailView
class ArticleDetailView(DetailView):
# 表示するページのhtmlの指定
template_name = 'articles/article_detail.html'
# modelの指定
model = Articles
# オブジェクト名の指定
context_object_name = 'article'
記事の編集画面
views.py
に追記をしていきます。
from django.views.generic.edit import UpdateView
class ArticleUpdateView(UpdateView):
# 表示するページのhtmlの指定
template_name = 'articles/article_edit.html'
# modelの指定
model = Articles
# form_classの指定
form_class = ArticleForm
# 取得するデータに関する部分
def get_context_data(self, **kwargs):
kwargs['article_id'] = self.object.pk
return super().get_context_data(**kwargs)
記事の編集内容確認画面
views.py
に追記をしていきます。
from django.shortcuts import get_object_or_404
class ArticleUpdateConfirmView(View):
# HTTPの要求への応答を処理する。
def post(self, request, *args, **kwargs):
article_instance = get_object_or_404(Articles, pk=kwargs['pk'])
form = ArticleForm(request.POST or None, instance=article_instance)
# テンプレートに渡すのに必要
context = {'form': form, 'article': article_instance, 'article_id': kwargs['pk']}
if 'confirm' in request.POST:
return render(request, 'articles/article_edit_confirm.html', context)
if 'back' in request.POST:
return render(request, 'articles/article_cedit.html', context)
if 'create' in request.POST:
# フォームのバリデーションを実行
if form.is_valid():
form.save()
return redirect('home')
else:
return render(request, 'articles/article_edit.html', context)
記事の登録内容確認画面の部分と似た感じになっています。
記事の削除確認画面
views.py
に追記をしていきます。
from django.views.generic.edit import DeleteView
class ArticleDeleteView(DeleteView):
# 表示するページのhtmlの指定
template_name = 'articles/article_delete.html'
# modelの指定
model = Articles
# 登録成功時のアクション
success_url = reverse_lazy("articles:create_list")
# オブジェクト名の指定
context_object_name = 'article_delete'
テンプレート
テンプレートに渡すデータの準備ができたので、テンプレートを用意しましょう!
articles/templates/articles
内に、空のhtmlファイルを一気に作っておきましょう。
ファイル名はviews.py
に書き入れたものと一致させてください。
Django
┗ articles
└ templates
└ articles
├ base.html
├ articles_list.html
├ article_create.html
├ article_create_confirm.html
├ article_detail.html
├ article_edit.html
├ article_edit_confirm.html
└ article_delete.html
ベースファイル base.html
まずbase.html
を編集します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blogアプリ</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
</body>
</html>
今回は、CSSにBootstrapを利用します。
<body>
タグの中身の一例です。
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a href="{% url 'articles:articles_list' %}" class="navbar-brand">Blogアプリ</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="{% url 'articles:article_create' %}">記事を追加</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">{% block content %} {% endblock %}</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"></script>
動的にしたい部分は{% %}
で囲めば大丈夫です。
articles:
はarticles/urls.py
で設定した名前空間を示しています。
base.html
で記述しているのは、ヘッダー部分と、メインコンテンツの枠組みのみです。
記事の一覧画面 articles_list.html
次にarticles_list.html
を編集しましょう。まずは枠組みです。
{% extends 'articles/base.html' %} {% block content %}
{% endblock %}
base.html
を継承して、content
と名のつくブロックを上書きするということが書いてあります。
書き方自体は自由ですが、今回はテーブルで作ってみようと思います。上記の内側に以下を追記してください。
<h2 class="m-2 border-bottom">記事一覧</h2>
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col">タイトル</th>
<th scope="col">本文</th>
<th scope="col">作成日時</th>
<th scope="col">最終更新日時</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
tbody
の中に、データベースからデータを持ってきて表示させたいので、ループ処理を入れます。tbody
の中に以下を追記してください。
{% for article in articles_list %}
<tr>
<td>
<div class="overflow-hidden" style="height: 70px; max-width: 300px;">{{ article.title }}</div>
</td>
<td>
<div class="overflow-hidden" style="height: 70px; max-width: 300px;">{{ article.article_body }}</div>
</td>
<td>
{{ article.created_at }}
</td>
<td>
{{ article.updated_at }}
</td>
<td>
<a class="btn btn-primary" href="{% url 'articles:article_detail' article.pk %}">詳細</a>
</td>
</tr>
{% endfor %}
pythonのfor処理と似た感じで書けます。
ここまで書くと、以下のようになっているはずです。
{% extends 'articles/base.html' %} {% block content %}
<h2 class="m-2 border-bottom">記事一覧</h2>
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col">タイトル</th>
<th scope="col">本文</th>
<th scope="col">作成日</th>
<th scope="col">最終更新日</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for article in articles_list %}
<tr>
<td>
<div class="overflow-hidden" style="height: 70px; max-width: 300px;">{{ article.title }}</div>
</td>
<td>
<div class="overflow-hidden" style="height: 70px; max-width: 300px;">{{ article.article_body }}</div>
</td>
<td>
{{ article.created_at }}
</td>
<td>
{{ article.updated_at }}
</td>
<td>
<a class="btn btn-primary" href="{% url 'articles:article_detail' article.pk %}">詳細</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
記事の登録画面 article_create.html
次は記事の登録画面を作りましょう。article_create.html
を編集します。枠組みはさっきと同じです。
{% extends 'articles/base.html' %} {% load widget_tweaks %} {% block content %}
<h2 class="m-2 border-bottom">新規記事作成</h2>
<form class="m-3" action="{% url 'articles:article_create_confirm' %}" method="post">
{% csrf_token %}
<div class="mb-3">
<label for="titleForm" class="form-label">{{ form.title.label }}</label>
{{ form.title | add_class:'form-control' }}
</div>
<div class="mb-3">
<label for="bodyForm" class="form-label">{{ form.content.article_body }}</label>
{{ form.article_body | add_class:'form-control' }}
</div>
<input class="btn btn-success" type="submit" name="confirm" value="投稿内容確認">
</form>
{% endblock %}
{% csrf_token %}
はセキュリティ上必要な呪文だと思って書きましょう。役割について知りたい方はこちらのnoteを参照してください。
form.py
で指定した内容が反映されます。
記事の登録内容確認画面 article_create_confirm.html
次は記事の登録内容確認画面を作りましょう。article_create_confirm.html
を編集します。枠組みはこれまでと同じです。
{% extends 'articles/base.html' %} {% block content %}
<h2 class="m-2 border-bottom">投稿内容確認</h2>
<p class="m-3">以下の内容で投稿します</p>
<div class="mx-3 card">
<div class="card-body">
<h3 class="card-title">{{ form.title.value }}</h3>
<p class="card-text">{{ form.article_body.value }}</p>
<small class="card-text text-body-secondary text-muted">文字数: {{ form.article_body.value | length }}</small>
</div>
</div>
<form method="post" action="{% url 'articles:article_create_confirm' %}" class="m-3">
{% csrf_token %}
{% for field in form %} {{ field.as_hidden }} {% endfor %}
<input class="btn btn-success" type="submit" name="create" value="投稿">
<input class="btn btn-warning" type="submit" name="back" value="戻る">
</form>
{% endblock %}
forの部分は、隠しパラメータで値を引き継いでいる部分です。
記事の詳細画面 article_detail.html
次は記事の詳細画面を作りましょう。article_detail.html
を編集します。枠組みはこれまでと同じです。
{% extends 'articles/base.html' %} {% block content %}
<h2 class="m-2 border-bottom">投稿詳細</h2>
<div class="card m-3">
<div class="card-body">
<h3 class="card-title">{{ article.title }}</h3>
<textarea class="card-text form-control-plaintext" readonly>{{ article.article_body }}</textarea>
<small class="card-text text-body-secondary text-muted">最終更新日時:<time>{{ article.updated_at }}</time></small><br>
<small class="card-text text-body-secondary text-muted">作成日時:<time>{{ article.created_at }}</time></small>
<div class="card-text">
<a href="{% url 'articles:article_edit' article.pk %}" class="btn btn-success">編集</a>
<a href="{% url 'articles:article_delete' article.pk%}" class="btn btn-danger">削除</a>
</div>
</div>
</div>
{% endblock %}
記事の編集画面 article_edit.html
次は記事の編集画面を作りましょう。article_edit.html
を編集します。枠組みはこれまでと同じです。
{% extends 'articles/base.html' %} {% load widget_tweaks %} {% block content %}
<h2 class="m-2 border-bottom">投稿を編集</h2>
<form class="m-3" action="{% url 'articles:article_edit_confirm' article_id %}" method="post">
{% csrf_token %}
<div class="mb-3">
<label for="titleForm" class="form-label">{{ form.title.label }}</label>
{{ form.title | add_class:'form-control' }}
</div>
<div class="mb-3">
<label for="bodyForm" class="form-label">{{ form.content.article_body }}</label>
{{ form.article_body | add_class:'form-control' }}
</div>
<input class="btn btn-success" type="submit" name="confirm" value="投稿内容確認">
</form>
{% endblock %}
article_create.html
の内容をコピーして必要な部分を編集すると楽に書けると思います。
action
のarticle_id
はviews.py
に記述した引数の名前です。
記事の編集内容確認画面 article_edit_confirm.html
次は記事の編集内容確認画面を作りましょう。article_edit_confirm.html
を編集します。枠組みはこれまでと同じです。
{% extends 'articles/base.html' %} {% block content %}
<h2 class="m-2 border-bottom">編集内容確認</h2>
<p class="m-3">以下の内容に更新します</p>
<div class="mx-3 card">
<div class="card-body">
<h3 class="card-title">{{ form.title.value }}</h3>
<p class="card-text">{{ form.article_body.value }}</p>
<small class="card-text text-body-secondary text-muted">文字数: {{ form.article_body.value | length }}</small>
</div>
</div>
<form method="post" action="{% url 'articles:article_edit_confirm' article_id %}" class="m-3">
{% csrf_token %}
{% for field in form %} {{ field.as_hidden }} {% endfor %}
<input class="btn btn-success" type="submit" name="create" value="更新">
<input class="btn btn-warning" type="submit" name="back" value="戻る">
</form>
{% endblock %}
article_create_confirm.html
の内容をコピーして必要な部分を編集すると楽に書けると思います。
記事の削除確認画面 article_delete.html
最後に記事の削除確認画面を作りましょう。article_delete.html
を編集します。枠組みはこれまでと同じです。
{% extends 'articles/base.html' %} {% block content %}
<h2 class="m-2 border-bottom">投稿削除確認</h2>
<p class="m-3">以下の投稿を削除します</p>
<div class="mx-3 card">
<div class="card-body">
<h3 class="card-title">{{ article_delete.title }}</h3>
<p class="card-text">{{ article_delete.article_body }}</p>
<small class="card-text text-body-secondary text-muted">
最終更新日時:<time>{{ article_delete.updated_at }}</time>
</small><br>
<small class="card-text text-body-secondary text-muted">
作成日時:<time>{{ article_delete.created_at }}</time>
</small>
</div>
</div>
<form method="post" class="m-3">
{% csrf_token %}
<input class="btn btn-danger" type="submit" name="create" value="削除">
<a class="btn btn-warning" href="{% url 'articles:article_detail' article_delete.pk %}">戻る</a>
</form>
{% endblock %}
admin.py
もう開発は大詰めです。ここからはサイトを管理するページを設定していきます。
articles
ディレクトリにadmin.py
を作り、記述していきます。
from django.contrib import admin
from .models import Articles
admin.site.register(Articles)
これでモデルクラスが表示されるようになりました。
管理者ユーザーの作成
localhost/admin
にアクセスすると、ログイン画面が開きます。
しかし、まだユーザーを作っていないので入れません。ここから作っていきましょう。
ターミナルにて以下のコマンドを実行します。
python3 manage.py createsuperuser
するとユーザー名、メールアドレス、パスワードの入力を求められます。
今回は適当に、
- admin
- <dummy>admin</dummy>@sample.com
- adminpass
としましょう。パスワードが簡単すぎると怒られますが、y
を入力すればそのまま登録できます。
テスト
articles/tests.py
これ結構大事らしいんですが、今回は使わない予定なので、一旦削除しちゃって大丈夫です。
tests.py
でテストを実行したい方はこちらの記事を参考にしてください。
手動によるテスト
手動でのテストは難しくないのでやりましょう。
- 実装機能の洗い出し
箇条書きで洗い出しましょう。と言いつつ、冒頭で既に出しているので再掲になります。今回実装した機能は以下の通りです。
- 記事の一覧画面( / と /articles)
- ヘッダーの表示
- トップへのリンク
- 記事作成ページへ遷移できるリンク
- タイトル/本文/最終更新日時/作成日時を表示
- 記事は最終更新日時の新しいものから順に並べる
- 詳細画面へ遷移できるリンク
- ヘッダーの表示
- 記事の登録画面(/articles/create)
- 入力フォーム
- 全要素必須
- 文字数
- 投稿内容確認ボタン
- 入力フォーム
- 記事の登録内容確認画面(/articles/create_confirm)
- 入力内容の表示
- 文字数の表示
- 戻るボタン
- 投稿ボタン
- 記事の編集画面(/articles/{id}/edit)
- 編集フォーム
- 編集対象のレコードが初期値で入力されている
- バリデーション
- 全要素必須
- 文字数
- 投稿内容確認ボタン
- 戻るボタン
- 投稿ボタン
- 編集フォーム
- 記事の編集内容確認画面(/articles/{id}/edit_confirm)
- 入力内容の表示
- 戻るボタン
- 投稿ボタン
- 記事の詳細画面(/articles/{id})
- タイトル/本文/最終更新日時/作成日時を表示
- 編集画面へのリンク
- 削除確認画面へのリンク
- 記事の削除確認画面(/articles/{id}/delete_confirm)
- 削除対象のタイトル/本文/最終更新日時/作成日時を表示
- 戻るボタン
- 削除ボタンを押下で削除実行
- 404ページ
- 存在しないidをURL指定すると表示
- テストケースの洗い出し
洗い出した機能に求められる要件を洗い出しましょう。スプレッドシートなどでまとめるとリストアップもテストもしやすいと思います。
終わり!
テストも無事終了したら、ブログアプリ作成完了です。
お疲れ様でした!
参考
- Macでdockerを使う際にlocalhostでcontainerに繋げない問題の調査 #Docker - Qiita
- Djangoでmakemigrationsコマンドを実行しても、No changes detectedと言われる場合の対処法 - 自動化無しに生活無し
- DjangoのFormの値の取得方法: is_validとcleaned_data
- Django 複数のアプリを持たせる場合URL名に名前空間を設定する方が幸せになれる | ガネーシャ様のアンテナショップ
- [Django] マイグレーションに関する操作
- https://nextribe.co.jp/fukuchan/python-django-crud
- はじめに · Bootstrap v5.0
- 【Django入門】自作で簡単なブログを作ってDjangoの1歩を踏み出してみよう ~Djangoチュートリアル~ - YouTube
- 【ソースコード付き】Django テンプレート 使い方まとめ|Shogo Saito
- 【71日目】Djangoのcsrf_tokenとは?_プログラミング学び日記|モリユウキ|YM202110
- 【Django】formのTextareaのサイズ変更の方法 | Free Hero Blog
- 【Django】django-widget-tweaksを使ってフォームにCSSや属性を簡単に追加する方法 | RyougoDesgin
- Djangoのフォームに入力した値を隠しパラメータで引き継ぐ - 今川館
- django開発メモ admin.py #Django - Qiita
- Djangoのadmin画面でSitesが表示されない - 親バカエンジニアのナレッジ帳
- 【入門】Django/tests.pyの書きかたとテスト実行のやりかた【重要】 | ジユーズ
Discussion