はじめに
今回はDjangoを使って友達リスト的なアプリを作成しようと思います。
プロジェクト名はTomodachi
メモ機能実装(友達とのリレーションを貼る)
# ブランチを切り替える
$ git branch feature-memo
$ git checkout feature-memo
メモモデル作成
friendslist/models.py
# 友達対メモ(1対多)のリレーションで作成
class Memo(models.Model):
text = models.CharField(default="", max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
friend = models.ForeignKey(Friend, on_delete=models.CASCADE)
# マイグレーションする
friendslist/admin.py
# 管理画面を投稿できるようにする
from friendslist.models import User, Friend, Category, Memo
admin.site.register(Memo)
友達詳細画面にてメモを表示する
friendslist/views.py
# メモを表示する記述をする
from friendslist.models import Friend, Category, Memo
def friend(request, pk):
if request.method == 'POST':
friend = Friend.objects.get(pk=pk)
form = FriendForm(request.POST, instance=friend)
if form.is_valid():
friend.save()
user = request.user
categories = Category.objects.filter(user=user)
friend = Friend.objects.get(pk=pk)
memos = Memo.objects.filter(friend=friend) ←☆追加する
context = {
'categories': categories,
'friend': friend,
'memos': memos, ←☆追加する
}
return render(request, 'friendslist/friend.html', context)
friend.html
# メモを表示する
<div class="column col-md-8">
<h2>友達メモ</h2>
<ul class="list-group">
{% for memo in memos %}
<li class="list-group-item">{{ memo.text }}</li>
{% endfor %}
</ul>
</div>
友達詳細画面にてメモを作成する
config/urls.py
# URLを設定する
path('<slug:pk>/memo/create/', views.memo_create, name="memo_create"),
friendslist/forms.py
# フォームを設定する
from friendslist.models import Friend, Category, Memo
class MemoForm(forms.ModelForm):
class Meta:
model = Memo
fields = (
'text',
)
friendslist/views.py
# メモを作成する記述をする
from friendslist.forms import FriendForm, UserCreationForm, CategoryForm, MemoForm
def memo_create(request, pk):
if request.method == 'POST':
form = MemoForm(request.POST)
friend = Friend.objects.get(pk=pk)
if form.is_valid():
memo = form.save(commit=False)
memo.friend = friend
memo.save()
return redirect('/{}/'.format(pk))
context = {}
return render(request, 'friendslist/memo/create.html', context)
friend.html
# メモ作成ページへのリンクを貼る
<a class="btn btn-info" href="{% url 'memo_create' friend.pk %}" role="button">メモを登録する</a>
memo/create.html
# メモ作成ページを作る
{% extends 'friendslist/base.html' %}
{% block content %}
<main class="container mb-5">
<form class="row" method="POST">
{% csrf_token %}
<div class="col col-md-8">
<h2>メモ作成</h2>
<ul class="list-group">
<li class="list-group-item">
<label class="form-label">メモ</label>
<textarea name="text" class="form-control" id="id_text" cols="30" rows="10" placeholder="ここにメモを書いてください"></textarea>
</li>
<li class="list-group-item">
<button type="submit" class="btn btn-success float-end">保存</button>
</li>
</ul>
</div>
</form>
</main>
{% endblock %}
メモを削除する機能を実装する
config/urls.py
# URLを設定
path('<slug:pk>/memo/<slug:memo_pk>/delete/', views.memo_delete, name="memo_delete"),
friendslist/views.py
# 削除する記述をする
def memo_delete(request, pk, memo_pk):
try:
memo = Memo.objects.get(pk=memo_pk)
except Memo.DoesNotExist:
raise Http404
memo.delete()
return redirect('/{}/'.format(pk))
```friend.html
# メモを削除するボタンを作成する
<ul class="list-group">
{% for memo in memos %}
<li class="list-group-item">
<a href="{% url 'memo_delete' friend.pk memo.pk %}" class="btn btn-outline-secondary btn-sm">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-archive" viewBox="0 0 16 16">
<path d="M0 2a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1v7.5a2.5 2.5 0 0 1-2.5 2.5h-9A2.5 2.5 0 0 1 1 12.5V5a1 1 0 0 1-1-1V2zm2 3v7.5A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5V5H2zm13-3H1v2h14V2zM5 7.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z"/>
</svg>
</a>
{{ memo.text }}
</li>
{% endfor %}
</ul>
見た目の修正
フッター削除
base.html
# footerの削除(以下を削除)
{% include 'friendslist/snippets/footer.html' %}
base_auth.html
# footerの削除(以下を削除)
{% include 'friendslist/snippets/footer.html' %}
認証画面修正
auth.html
# タイトル見た目修正
<h1 class="display-4 fst-italic text-center "><a href="/" class="text-decoration-none">Tomodachi</a></h1>
# サインインボタンを修正
<div class="form-label-group my-3 d-flex justify-content-center">
<button class="btn btn-primary btn-block" type="submit">
{% if 'login' in request.path %}
サインイン
{% elif 'signup' in request.path %}
サインアップ
{% endif %}
</button>
</div>
ヘッダーを修正
snippets/header.html
# ヘッダのリンクと認証の有無で表示の有無
{% if user.is_authenticated %}
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" aria-current="page" href="/">友達一覧</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/create/">友達登録</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/category/{{ first_category.pk }}/">カテゴリ一覧</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/category/create/" tabindex="-1" aria-disabled="true">カテゴリ登録</a>
</li>
</ul>
</div>
{% endif %}
友達一覧ページ
index.html
# ふりがながあれば表示する
{% if friend.furigana is not None %}
({{ friend.furigana }})
{% endif %}
友達詳細ページ
friendslist/views.py
# 誕生日をinputに表示させる(フォーマット変更)
friend_birthday= "{0:%Y-%m-%d}".format(friend.birthday)
friend.html
# 誕生日をinputに表示させる(フォーマット変更)
<li class="list-group-item">
<label class="form-label">誕生日</label>
<input type="date" class="form-control" id="id_birthday" name="birthday" value="{{ friend_birthday }}">
</li>
友達作成ページ
friendslist/create.html
# 友達メモの部分を削除する(以下の部分を削除)
<div class="column col-md-8">
<h2>友達メモ</h2>
<ul class="list-group">
<li class="list-group-item">メモ1</li>
<li class="list-group-item">メモ2</li>
<li class="list-group-item">メモ3</li>
</ul>
</div>
カテゴリ一覧ページ
category/index.html
# カテゴリ削除ボタンを修正
<a href="{% url 'category_delete' category.pk %}" class="btn btn-danger btn-sm float-end">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-archive" viewBox="0 0 16 16">
<path d="M0 2a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1v7.5a2.5 2.5 0 0 1-2.5 2.5h-9A2.5 2.5 0 0 1 1 12.5V5a1 1 0 0 1-1-1V2zm2 3v7.5A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5V5H2zm13-3H1v2h14V2zM5 7.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z"/>
</svg>
</a>
# ふりがな表示
{% if friend.furigana is not None %}
({{ friend.furigana }})
{% endif %}
リンクボタンを真ん中寄せにする
snippets/linkbtns.html
# リンクボタンを真ん中に寄せる
<div class="col d-flex justify-content-center">
<a class="btn btn-danger" href="/" role="button">友達一覧</a>
</div>
<div class="col d-flex justify-content-center">
<a class="btn btn-success" href="/create/" role="button">友達登録</a>
</div>
<div class="col d-flex justify-content-center">
<a class="btn btn-warning" href="/category/{{ first_category.pk }}/" role="button">カテゴリ一覧</a>
</div>
<div class="col d-flex justify-content-center">
<a class="btn btn-info" href="/category/create/" role="button">カテゴリ登録</a>
</div>
さいごに
今回はメモのリレーションをしました。
メモの一覧表示機能
メモの作成機能
メモの削除機能
見た目のリファクタリングをしました。
base.htmlの修正
認証画面の修正
ヘッダーの修正
友達一覧表示
友達詳細表示
友達作成
カテゴリ一覧表示
リンクボタン
あと、サインアップ後に一覧表示できないエラーがおきました。
→この原因はユーザー作成時にカテゴリが作成されていないことが原因のようです。
→つまり、ユーザー作成時にデフォルトカテゴリを作成する機能を実装する必要がありそうです。