【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