VSCodeで動作するLaTeX環境の構築

初めに

今回は卒業研究の論文執筆で用いるLaTeXの環境構築を行いました。また、VSCode上で手軽に操作できるようにしました。
これに対して行った具体的な手順をまとめます。

概要

  • 必要なソフトウェアやサービス
  • リポジトリのクローンとDocker Imageの起動
  • 文書作成と表示

必要なソフトウェアやサービス

まず初めに、環境構築するにあたり以下のソフトウェアやサービスを用意しました。

  • VSCode
  • GitHubアカウント
  • Docker(WSL上でインストール)

次に、以下のコマンドを用いて今回使用するDocker Imageをローカルにpullしました。

$ docker pull ghcr.io/being24/latex-docker:latest

続いて、VSCode拡張機能より以下の画像のものをインストールしました。
また、この時にインストールする環境はUbuntuと接続したリモート環境で行いました。

リモート環境の構築は以下の記事より行っています。
nissin-geppox.hatenablog.com

リポジトリのクローンとDocker Imageの起動

次に、今回使用させてもらう以下のリポジトリをローカルにクローンしました。
GitHub - being24/latex-template-ja: できるだけ簡単にLaTeX環境を構築できるようにするテンプレート

続いて、以下のコマンドよりDockerとDocker Imageを起動しました。

$ sudo service docker start
$ docker run -it ghcr.io/being24/latex-docker

また、DockerやDocker Imageの詳しい内容は以下の記事にまとめています。
nissin-geppox.hatenablog.com

文書作成と表示

続いて、リポジトリからクローンしたものに含まれているmain.texファイルを以下のように変更しました。

\documentclass{classes/resume}

\title{
	\gt{LaTeX 環境の構築}\\
}

\author{
	\gt{○○大学 ××学部 △△学科}\\
	\gt{羅手 太郎}
}

\date{}

\begin{document}
\maketitle

\end{document}

次に、上記で作成したmain.texをビルドしました。
ビルドにはインストールした拡張機能を使用します。
以下の画像は拡張機能のコマンド画面です。
この画面の緑の三角マークをクリックすることによりビルドを行うことができます。

ビルドの実行が完了するとディレクトリ内にpdfファイルが生成されます。
上の画像の"View in web browser"よりブラウザ上で完成した文書を確認することができます。

以下の画像は"View in web browser"を用いてブラウザで表示したときの様子です。

上の画像より、正常にビルドが完了し、ブラウザ上で閲覧できることを確認することができました。

Django レビュー機能の実装

初めに

前回に続いて本棚アプリケーションの作成を行いました。今回は投稿された本に対してレビューをすることができる機能を実装しました。
これに対して行った具体的な手順をまとめます。
nissin-geppox.hatenablog.com

概要

  • レビュー投稿画面の作成
  • 本の情報の表示

レビュー投稿画面の作成

まず初めに、レビュー投稿機能のルーティングをurls.pyで以下のように設定しました。

from django.urls import path
from . import views
urlpatterns = [
  path('', views.index_view, name='index'),
  path('book/', views.ListBookView.as_view(), name='list-book'),
  path('book/<int:pk>/detail/', views.DetailBookView.as_view(), name='detail-book'),
  path('book/create/', views.CreateBookView.as_view(), name='create-book'),
  path('book/<int:pk>/delete/', views.DeleteBookView.as_view(), name='delete-book'),
  path('book/<int:pk>/update/', views.UpdateBookView.as_view(), name='update-book'),
  path('book/<int:book_id>/review/', views.CreateReview.as_view(), name='review'),
]

次に、以下のようにレビュー投稿用のモデルをmodels.pyに実装しました。

from django.db import models
from .consts import MAX_RATE

RATE_CHOICES = [(x, str(x)) for x in range(0, MAX_RATE + 1)]

CATEGORY = (('business', 'ビジネス'), ('life', '生活'), ('other', 'その他'))

class Book(models.Model):
  title = models.CharField(max_length=100)
  text = models.TextField()
  category = models.CharField(
    max_length=100,
    choices = CATEGORY
  )

  def __str__(self):
    return self.title

class Review(models.Model):
  book = models.ForeignKey(Book, on_delete=models.CASCADE)
  title = models.CharField(max_length=100)
  text = models.TextField()
  rate = models.IntegerField(choices=RATE_CHOICES)
  user = models.ForeignKey('auth.User', on_delete=models.CASCADE)

  def __str__(self):
    return self.title

続けて、このモデルで使用する定数を格納するファイルを以下のコマンドより生成しました。

$ touch book/consts.py

ここで作成したconsts.pyにモデルで使用する定数を以下のように追加しました。

MAX_RATE = 5

次に、以下のコマンドより作成したモデルをもとにテーブルを作成しました。
また、同時にレビュー画面を表示するhtmlファイルを生成しました。

$ python3 manage.py makemigrations
$ python3 manage.py migrate

$ touch book/templates/book/review_form.html

続いて、生成したreview_form.htmlに以下のようなコードを追加しました。

{% extends 'base.html' %}

{% block title %}レビュー投稿{% endblock %}
{% block h1 %}レビュー投稿{% endblock %}
{% block content %}
<form method="post" class="p-4 m-4 bg-light border border-success rounded form-group">
  {% csrf_token %}
  <label>
    対象書籍
  </label>
  <input class="form-control" value="{{ book.title }}" readonly>
  <label>
    タイトル
  </label>
  <input class="form-control" name="title">
  <label>
    本文
  </label>
  <textarea class="form-control" name="text" rows="3"></textarea>
  <label>
    星の数
  </label>
  <select class="form-control" name="rate">
    <option value="0">0 (最低)</option>
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3" selected>3 (普通)</option>
    <option value="4">4</option>
    <option value="5">5 (最高)</option>
  </select>
  <input type="hidden" name='book' value="{{ book.id }}">
  <button type="submit" class="btn btn-success mt-4">投稿する</button>
</form>
{% endblock %}

最後に、サーバーを起動しレビュー投稿画面を表示しました。

上の画像より、正常に投稿画面が表示されていることを確認することができました。

本の情報の表示

続いて、レビュー投稿画面に異なるモデルのデータである書籍の情報を表示できるようにしました。

まず、views.pyを以下のように変更しました。

from django.shortcuts import render, redirect
from django.urls import reverse_lazy
from django.urls import reverse, reverse_lazy
from django.views.generic import (
  ListView,
  DetailView,
  CreateView,
  DeleteView,
  UpdateView,
)
from .models import Book
from .models import Book, Review


class ListBookView(ListView):
@@ -38,3 +38,22 @@ class UpdateBookView(UpdateView):
def index_view(request):
  object_list = Book.objects.order_by('category')
  return render(request, 'book/index.html',{'object_list': object_list})

class CreateReview(CreateView):
  model = Review
  fields = ('book', 'title', 'text', 'rate')
  template_name = 'book/review_form.html'

  def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['book'] = Book.objects.get(pk=self.kwargs['book_id'])
    print(context)
    return context

  def form_valid(self, form):
    form.instance.user = self.request.user

    return super().form_valid(form)

  def get_success_url(self):
    return reverse('detail-book', kwargs={'pk': self.object.

また、管理画面からReviewモデルのデータを追加できるようにadmin.pyを以下のように変更しました。

from django.contrib import admin
from .models import Book, Review

admin.site.register(Book)
admin.site.register(Review)

続いて、使用できるようになった本のデータを表示できるようreview_form.htmlを以下のように変更しました。

-- 一部省略 --
<form method="post" class="p-4 m-4 bg-light border border-success rounded form-group">
  {% csrf_token %}
  <label>
    対象書籍
  </label>
  <input class="form-control" value="{{ book.title }}" readonly>
  <label>
    タイトル
  </label>
  <input class="form-control" name="title">
  <label>
    本文
  </label>
  <textarea class="form-control" name="text" rows="3"></textarea>
  <label>
    星の数
  </label>
  <select class="form-control" name="rate">
    <option value="0">0 (最低)</option>
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3" selected>3 (普通)</option>
    <option value="4">4</option>
    <option value="5">5 (最高)</option>
  </select>
  <input type="hidden" name='book' value="{{ book.id }}">
  <button type="submit" class="btn btn-success mt-4">投稿する</button>
</form>
{% endblock %}

以下の画像は変更が加えられた後のレビュー投稿画面の様子です。

上の画像より、対象書籍の欄に他のモデルの情報が表示されていることを確認することができました。

最後に、詳細画面からレビュー投稿画面へ遷移できるようにbook_detail.htmlを以下のように変更しました。

-- 一部省略 --
<div class="p-4 m-4 bg-light border border-success rounded">
  <h2 class="text-success">{{ object.title }}</h2>
  <p>{{ object.text }}</p>
  <a href="{% url 'review' object.pk %}" class="btn btn-primary">レビューする</a>
  <a href="{% url 'list-book' %}" class="btn btn-primary">一覧へ</a>
  <a href="{% url 'update-book' object.pk %}" class="btn btn-primary">編集する</a>
  <a href="{% url 'delete-book' object.pk %}" class="btn btn-primary">削除する</a>
  <h6 class="card-title">{{ object.category }}</h6>
</div>
{% endblock content %}

Django ユーザのログイン・ログアウト機能の実装

初めに

前回に続いて本棚アプリケーションの作成を行いました。今回はユーザのログイン・ログアウト機能の実装を行いました。
これに対して行った具体的な手順をまとめます。
nissin-geppox.hatenablog.com

概要

  • ログイン機能の実装
  • ログアウト機能の実装
  • アプリケーションaccountsの作成
  • 会員登録機能の実装

ログイン機能の実装

まず初めに、ログイン・ログアウト機能の実装に際してルーティングの設定をurls.pyに対して以下のような変更を行いました。

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('django.contrib.auth.urls')),
    path('', include('book.urls'))
]

次に、ログイン画面用のhtmlを作成します。
以下のコマンドよりhtmlファイルを生成しました。

$ mkdir templates/registration
$ touch templates/registration/login.html

次に、生成したlogin.htmlに以下のようなコードを追加しました。

{% extends 'base.html' %}

{% block content %}
<h1>ログイン</h1>
<form method="post" class="p-4 m-4 bg-light border border-success rounded form-group">
  {% csrf_token %}
  {% for error in form.errors.values %}
  {{ error }}
  {% endfor %}
  <label>
    ユーザID
  </label>
  <input class="form-control" name="username">
  <label>
    パスワード
  </label>
  <input type="password" class="form-control" name="password">
  <button type="submit" class="btn btn-success mt-4">ログインする</button>
</form>
{% endblock %}

また、以下のようにログイン完了時のURLの設定をsetting.pyにて行いました。

-- 一部省略 --

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

LOGIN_REDIRECT_URL ='list-book'

以下の画像は作成したログイン画面の様子です。

上の画像より、作成したログイン画面が正常に表示されることを確認することができました。

また、以下の画像はまだ登録をしていないユーザを入力し、ログインを試みたときの様子です。

上の画像より、たしかに、登録したユーザしかログインできないことを確認することができました。

ログアウト機能の実装

続いて、ログアウト機能を実装しました。
まず、以下のようにurls.pyに対してルーティングの設定を行いました。

from django.urls import path
from . import views
urlpatterns = [
  path('', views.index_view, name='index'),
  path('book/', views.ListBookView.as_view(), name='list-book'),
  path('book/<int:pk>/detail/', views.DetailBookView.as_view(), name='detail-book'),
  path('book/create/', views.CreateBookView.as_view(), name='create-book'),
  path('book/<int:pk>/delete/', views.DeleteBookView.as_view(), name='delete-book'),
  path('book/<int:pk>/update/', views.UpdateBookView.as_view(), name='update-book'),
  path('logout/', views.logout_view, name='logout'),

次に、以下のようにログアウトとredirect機能をviews.pyに追加しました。

from django.shortcuts import render, redirect
from django.contrib.auth import logout
from django.urls import reverse_lazy
from django.views.generic import (
  ListView,
  DetailView,
  CreateView,
  DeleteView,
  UpdateView,
)
from .models import Book

-- 一部省略 --

  return render(request, 'book/index.html',{'object_list': object_list})

def logout_view(request):
  logout(request)
  return redirect('index')

続いて、以下のようにログアウトのリンクをbase.htmlに追加しました。

-- 一部省略 --

<body>
  <nav class="navbar navbar-dark bg-success sticky-top">
    <div class="navbar-nav d-flex flex-row">
      <a class="nav-link mx-3" href="{% url 'list-book' %}">書籍一覧</a>
      <a class="nav-link mx-3" href="{% url 'create-book' %}">書籍登録</a>
    </div>
    <div class="navbar-nav d-flex flex-row">
      {% if request.user.is_authenticated %}
      <a class="nav-link mx-3" href="{% url 'logout' %}">ログアウト</a>
      {% else %}
      <a class="nav-link mx-3" href="{% url 'login' %}">ログイン</a>
      {% endif %}
    </div>
  </nav>
  <div class="'p-4">
    <h1>{% block h1 %}{% endblock %}</h1>
    {% block content %}{% endblock content %}
  </div>
</body>
</html>

アプリケーションaccountsの作成

続いて、会員登録機能を追加するために以下のコマンドを用いて新たにアプリケーションaccountsを作成しました。

$ python3 manage.py startapp accounts

次に、作成したアプリケーションを反映させるためにsetting.pyに以下のようなコードを追加します。

-- 一部省略 --

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'book.apps.BookConfig',
    'accounts.apps.AccountsConfig',
]

-- 一部省略 --

また、以下のようにbookproject/urls.pyの内容を修正しました。

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('accounts.urls')),
    path('', include('book.urls'))
]

次に、以下のようにbook/urls.pyのlogoutに関する記述を修正しました。

from django.urls import path

from . import views

urlpatterns = [
  path('', views.index_view, name='index'),
  path('book/', views.ListBookView.as_view(), name='list-book'),
  path('book/<int:pk>/detail/', views.DetailBookView.as_view(), name='detail-book'),
  path('book/create/', views.CreateBookView.as_view(), name='create-book'),
  path('book/<int:pk>/delete/', views.DeleteBookView.as_view(), name='delete-book'),
  path('book/<int:pk>/update/', views.UpdateBookView.as_view(), name='update-book'),
]

最後に、views.pyに関しても以下のように修正しました。

from django.shortcuts import render, redirect
from django.urls import reverse_lazy
from django.views.generic import (
  ListView,
  DetailView,
  CreateView,
  DeleteView,
  UpdateView,
)
from .models import Book

-- 一部省略 --

def index_view(request):
  object_list = Book.objects.order_by('category')
  return render(request, 'book/index.html',{'object_list': object_list})

-- 以下を削除 --

会員登録機能の実装

続いて、会員登録機能を実装しました。
まず、以下のコマンドよりurls.pyファイルを生成しました。

$ touch accounts/urls.py

また、生成したurls.pyに以下のようなコードを追加しました。

from django.urls import path
from django.contrib.auth.views import LoginView, LogoutView

from .views import SignupView

app_name = 'accounts'

urlpatterns = [
  path('login/', LoginView.as_view(), name='login'),
  path('logout/', LogoutView.as_view(), name='logout'),
  path('signup/', SignupView.as_view(), name='signup'),
]

次に、以下のようにsetting.pyに対してログアウトしたときの遷移先を設定しました。

-- 一部省略 --

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

LOGIN_REDIRECT_URL ='index'
LOGOUT_REDIRECT_URL ='index'

次に、app_nameで宣言した文字列を以下のようにbase.htmlに反映させました。

-- 一部省略 --

    <div class="navbar-nav d-flex flex-row">
      {% if request.user.is_authenticated %}
      <a class="nav-link mx-3" href="{% url 'accounts:logout' %}">ログアウト</a>
      {% else %}
      <a class="nav-link mx-3" href="{% url 'accounts:login' %}">ログイン</a>
      {% endif %}
    </div>
  </nav>
  <div class="'p-4">
    <h1>{% block h1 %}{% endblock %}</h1>
    {% block content %}{% endblock content %}
  </div>
</body>

</html>

続いて、accountsのviews.pyを以下のように作成しました。

from django.shortcuts import render
from django.contrib.auth.models import User
from django.urls import reverse_lazy
from django.views.generic import CreateView

from .forms import SignupForm

class SignupView(CreateView):
  model = User
  form_class = SignupForm
  template_name = 'accounts/signup.html'
  success_url = reverse_lazy('index')

次に、views.pyで指定したforms.pyを作成します。
まず、以下のコマンドよりforms.pyを生成しました。

$ touch accounts/forms.py

続いて、生成したforms.pyに以下のようなコードを追加しました。

from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class SignupForm(UserCreationForm):
  class Meta:
    model = User
    fields = ('username',)

最後に、会員登録画面を作成しました。
まず、以下のコマンドより、会員登録画面用のhtmlを生成しました。

$ signup.html

続いて、生成したsignup.htmlに以下のようなコードを追加しました。

{% extends 'base.html' %}

{% block title %}アカウント作成{% endblock %}
{% block h1 %}アカウント作成{% endblock %}
{% block content %}
<form method="post" class="p-4 m-4 bg-light border border-success rounded form-group">
  {% csrf_token %}
  <input type="text" name='username' class="form-control my-4" placeholder="ユーザ ID">
  <input type="password" name='password1' class="form-control mt-4" placeholder="パスワード">
  <input type="password" name='password2' class="form-control mt-4" placeholder="パスワード確認用">
  <small class="mb-2 d-block text-start">パスワードは8文字以上で設定してください。</small>
  {% if form.errors %}
  <span class="mb-2 small text-danger d-block text-start">利用できないユーザ IDやパスワードの可能性があります。入力内容を再度ご確認ください。</span>
  {% endif %}
  <button type="submit" class="btn btn-success m-2">アカウント作成</button>
</form>
{% endblock %}

加えて、管理画面で会員登録の文字情報が表示されるようにbase.htmlを以下のように変更しました。

-- 一部省略 --

    <div class="navbar-nav d-flex flex-row">
      {% if request.user.is_authenticated %}
      <a class="nav-link mx-3" href="{% url 'accounts:logout' %}">ログアウト</a>
      {% else %}
      <a class="nav-link mx-3" href="{% url 'accounts:login' %}">ログイン</a>
      <a class="nav-link mx-3" href="{% url 'accounts:signup' %}">会員登録</a>
      {% endif %}
    </div>
  </nav>
  <div class="'p-4">
    <h1>{% block h1 %}{% endblock %}</h1>
    {% block content %}{% endblock content %}
  </div>
</body>

</html>

続いて、accountsのviews.pyを以下のように作成しました。

from django.shortcuts import render
from django.contrib.auth.models import User
from django.urls import reverse_lazy
from django.views.generic import CreateView

from .forms import SignupForm

class SignupView(CreateView):
  model = User
  form_class = SignupForm
  template_name = 'accounts/signup.html'
  success_url = reverse_lazy('index')

以下の画像は会員登録画面の様子です。

上の画像より、会員登録画面が正常に表示されることを確認することができました。

Django トップページとindexの作成

初めに

前回に続いて本棚アプリケーションの作成を行いました。今回はトップページの作成と次回から使用するindexの作成を行いました。
これに対して行った具体的な手順をまとめます。
nissin-geppox.hatenablog.com

概要

  • renderによるトップページの表示
  • index.htmlの作成

概要

まず初めに、トップページを表示するためのindex_viewのルーティングを以下のように追加しました。

from django.urls import path

from . import views

urlpatterns = [
  path('', views.index_view, name='index'),
  path('book/', views.ListBookView.as_view(), name='list-book'),
  path('book/<int:pk>/detail/', views.DetailBookView.as_view(), name='detail-book'),
  path('book/create/', views.CreateBookView.as_view(), name='create-book'),
  path('book/<int:pk>/delete/', views.DeleteBookView.as_view(), name='delete-book'),
  path('book/<int:pk>/update/', views.UpdateBookView.as_view(), name='update-book'),
]

次に、views.pyにindex_viewを以下のように定義しました。

-- 一部省略 --
class UpdateBookView(UpdateView):
  model = Book
  fields = ('title', 'text', 'category')
  template_name = 'book/book_update.html'
  success_url = reverse_lazy('list-book')

def index_view(request):
  return render(request, 'book/index.html',{'somedata': 100})

続いて、以下のコマンドよりindex.htmlを作成しました。

$ touch book/templates/book/index.html

次に、上記で生成したindex.htmlに以下のコードを追加しました。

{{ somedata }}

最後に作成したトップページを表示しました。

上の画像より、somedataに紐づいたデータが表示されていることを確認することができました。

index.htmlの作成

続いて、具体的にindex.htmlの中身を作成していきます。
まず、views.pyを以下のように変更しました。

-- 一部省略 --

class UpdateBookView(UpdateView):
  model = Book
  fields = ('title', 'text', 'category')
  template_name = 'book/book_update.html'
  success_url = reverse_lazy('list-book')

def index_view(request):
  object_list = Book.objects.all()
  return render(request, 'book/index.html',{'object_list': object_list})

続いて、index.htmlを以下のように変更しました。

{% extends 'base.html' %}

{% block title %}書籍一覧{% endblock %}
{% block h1 %}書籍一覧{% endblock %}

{% block content %}
{% for item in object_list %}
<div class="p-4 m-4 bg-light border border-success rounded">
  <h2 class="text-success">{{ item.title }}</h2>
  <h6>カテゴリ:{{ item.category }}</h6>
  <div class="mt-3">
    <a href="{% url 'detail-book' item.pk %}">詳細へ</a>
  </div>
</div>
{% endfor %}
{% endblock content %}

ここで、以下のコマンドを用いてサーバーを起動し、index.htmlにアクセスしました。

$ python3 manage.py runserver

以下の画像は表示された時の様子です。

上の画像より、index.htmlにより本の一覧が表示されることを確認することができました。

続いて、データを1つ追加し、もう一度トップページにアクセスしました。
以下の画像がその時の様子です。

上の画像より、正常にデータが追加されていることを確認することができました。

次に、表示されている3つのデータに対してカテゴリーごとに順番を並び替えるようにviews.pyを以下のように変更しました。

class UpdateBookView(UpdateView):
  model = Book
  fields = ('title', 'text', 'category')
  template_name = 'book/book_update.html'
  success_url = reverse_lazy('list-book')

def index_view(request):
  object_list = Book.objects.order_by('category')
  return render(request, 'book/index.html',{'object_list': object_list})

この変更により、以下のように表示されました。

上の画像より、カテゴリー順にデータが並び替えられていることを確認することができました。

Django ページ遷移とレイアウトの調整

初めに

前回に続いて本棚アプリケーションの作成を行いました。今回はページを遷移するためのリンクの作成、レイアウトの調整を行いました。また、データ更新の動作確認も行いました。
これに対して行った具体的な手順をまとめます。
nissin-geppox.hatenablog.com

概要

  • リンクの設定
  • データ更新の動作確認
  • レイアウトの調整

リンクの設定

初めに、一覧画面book_list.htmlに対してリンクの設定を以下のように行いました。

{% extends 'base.html' %}

{% block title %}書籍一覧{% endblock %}

{% block content %}
{% for item in object_list %}
<div class="card">
  <h5 class="card-header">{{ item.title }}</h5>
  <div class="card-body">
    <p class="card-text">{{ item.text }}</p>
    <a href="{% url 'detail-book' item.pk %}" class="btn btn-primary">詳細へ</a>
    <h6 class="card-title">{{ item.category }}</h6>
  </div>
</div>
{% endfor %}
{% endblock content %}

以下の画像は変更を加えた一覧画面の様子です。

上の画像より、詳細画面へ遷移するためのリンクが生成されているのを確認することができました。

次に、詳細画面book_detail.htmlに対してリンクの設定を以下のように行いました。

{% extends 'base.html' %}

{% block title %}書籍詳細{% endblock %}

{% block content %}
<div class="card">
  <h5 class="card-header"> {{ object.title }}</h5>
  <div class="card-body">
    <p class="card-text"> {{ object.text }}</p>
    <a href="{% url 'list-book' %}" class="btn btn-primary">一覧へ</a>
    <a href="{% url 'update-book' object.pk %}" class="btn btn-primary">編集する</a>
    <a href="{% url 'delete-book' object.pk %}" class="btn btn-primary">削除する</a>
    <h6 class="card-title">{{ object.category }}</h6>
  </div>
</div>
{% endblock content %}

以下の画像は変更を加えた詳細画面の様子です。

上の画像より、詳細画面、編集画面、削除画面へ遷移するためのリンクが生成されているのを確認することができました。

データ更新の動作確認

前回のデータの更新機能の実装に続き、その動作テストを行いました。
nissin-geppox.hatenablog.com
まず、以下の画像はデータの内容を変更する前の様子です。

これは、先ほど作成した詳細画面の様子と同一です。
次に、データに変更を加え、更新した様子は以下のようになりました。

上の画像より、データが正常に更新されていることを確認することができました。

レイアウトの調整

最後に一部のhtmlファイルに対してレイアウトの調整を行いました。
まず、テンプレートファイルであるbase.htmlに以下のようにヘッダーを追加しました。

<!DOCTYPE html>
<html lang="en">

<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.rtl.min.css"
    integrity="sha384-+4j30LffJ4tgIMrq9CwHvn0NjEvmuDCOfk6Rpg2xg7zgOxWWtLtozDEEVvBPgHqE" crossorigin="anonymous">

  <title>{% block title %}{% endblock title %}| 本棚アプリ</title>
</head>

<body>
  <nav class="navbar navbar-dark bg-success sticky-top">
    <div class="navbar-nav d-flex flex-row">
      <a class="nav-link mx-3" href="{% url 'list-book' %}">書籍一覧</a>
      <a class="nav-link mx-3" href="{% url 'create-book' %}">書籍登録</a>
    </div>
  </nav>
  <div class="'p-4">
    <h1>{% block h1 %}{% endblock %}</h1>
    {% block content %}{% endblock content %}
  </div>
</body>

</html>

次に、一覧画面book_list.htmlを以下のように変更しました。

{% extends 'base.html' %}

{% block title %}書籍一覧{% endblock %}
{% block h1 %}書籍一覧{% endblock %}

{% block content %}
{% for item in object_list %}
<div class="p-4 m-4 bg-light border border-success rounded">
  <h2 class="text-success">{{ item.title }}</h2>
  <h6>カテゴリ:{{ item.category }}</h6>
  <div class="mt-3">
    <a href="{% url 'detail-book' item.pk %}">詳細へ</a>
  </div>
</div>
{% endfor %}
{% endblock content %}

以下の画像は変更を加えた一覧画面の様子です。

上の画像より、正常にレイアウトが変更されていることを確認することができました。また、ヘッダーも確認することができました。

最後に、詳細画面book_detail.htmlを以下のように変更しました。

{% extends 'base.html' %}

{% block title %}{{ object.title }}{% endblock %}
{% block h1 %}書籍詳細{% endblock %}

{% block content %}
<div class="p-4 m-4 bg-light border border-success rounded">
  <h2 class="text-success">{{ object.title }}</h2>
  <p>{{ object.text }}</p>
  <a href="{% url 'list-book' %}" class="btn btn-primary">一覧へ</a>
  <a href="{% url 'update-book' object.pk %}" class="btn btn-primary">編集する</a>
  <a href="{% url 'delete-book' object.pk %}" class="btn btn-primary">削除する</a>
  <h6 class="card-title">{{ object.category }}</h6>
</div>
{% endblock content %}

以下の画像は変更を加えた詳細画面の様子です。

上の画像より、正常にレイアウトが変更されていることを確認することができました。

Django データの作成、削除、更新の実装

初めに

前回に続いて本棚アプリケーションの作成を行いました。今回はデータの作成、削除、更新の機能を実装しました。
これらに対して行った具体的な手順をまとめます。
nissin-geppox.hatenablog.com

概要

  • データの作成
  • データの削除
  • データの更新

データの作成

初めに、管理画面からではなくウェブページ上からデータを作成できるようにしました。
まず、以下のようにurls.pyにコードを追加してルーティングの設定を行いました。また、次のviews.pyで使用するreverse_lazyで引数として利用するために、設定したpathそれぞれに"name = "を用いて名前を設定しました。

from django.urls import path

from . import views

urlpatterns = [
  path('book/', views.ListBookView.as_view(), name='list-book'),
  path('book/<int:pk>/detail/', views.DetailBookView.as_view(),name='detail-book'),
  path('book/create/', views.CreateBookView.as_view(), name='create-book'),
]

続けて、以下のようにviews.pyにデータ作成用のclassを作成し、テーブルと項目を指定しました。加えて、最後の行では遷移先を設定しました。

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView
from .models import Book


class ListBookView(ListView):
  template_name = 'book/book_list.html'
  model = Book

class DetailBookView(DetailView):
  template_name = 'book/book_detail.html'
  model = Book

class CreateBookView(CreateView):
  template_name = 'book/book_create.html'
  model = Book
  fields = {'title', 'text', 'category'}
  success_url = reverse_lazy('list-book')

次に、データ作成画面のhtmlファイルbook_create.htmlを以下のように作成しました。

{% extends 'base.html' %}

{% block title %}書籍作成{% endblock %}

{% block content %}
<form method='POST'>{% csrf_token %}
  {{form.as_p}}
  <input type='submit' value='作成する'>
</form>
{% endblock content %}

以下の画像は表示されたデータ作成画面の様子です。

上の画像より、データ作成画面が正常に表示されることを確認することができました。
また、以下の画像はこの画面で作成したデータが追加された後の一覧画面の様子です。

上の画像より、正常にデータが追加されていることを確認することができました。

データの削除

次に、作成したデータを削除できるようにしました。
まず、以下のようにurls.pyにコードを追加してルーティングの設定を行いました。

from django.urls import path

from . import views

urlpatterns = [
  path('book/', views.ListBookView.as_view(), name='list-book'),
  path('book/<int:pk>/detail/', views.DetailBookView.as_view(),name='detail-book'),
  path('book/create/', views.CreateBookView.as_view(), name='create-book'),
  path('book/<int:pk>/delete/', views.DeleteBookView.as_view(), name='delete-book'),
]

続けて、以下のようにviews.pyにデータ削除用のclassを作成し、テーブルを指定しました。加えて、最後の行では遷移先を設定しました。

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, DeleteView
from .models import Book


class ListBookView(ListView):
  template_name = 'book/book_list.html'
  model = Book

class DetailBookView(DetailView):
  template_name = 'book/book_detail.html'
  model = Book

class CreateBookView(CreateView):
  template_name = 'book/book_create.html'
  model = Book
  fields = {'title', 'text', 'category'}
  success_url = reverse_lazy('list-book')

class DeleteBookView(DeleteView):
  template_name ='book/book_confirm_delete.html'
  model = Book
  success_url = reverse_lazy('list-book')

次に、データ削除画面のhtmlファイルbook_confirm_delete.htmlを以下のように作成しました。

{% extends 'base.html' %}

{% block title %}書籍削除{% endblock %}

{% block content %}
<form method='post'>
  {% csrf_token %}
  <button type='submit'>{{ object.title }}を削除する</button>
</form>
{% endblock %}

以下の画像は表示されたデータ削除画面の様子です。

上の画像より、データ削除画面が正常に表示されることを確認することができました。
また、以下の画像はデータが削除された後の一覧画面の様子です。

上の画像より、正常にデータが削除されていることを確認することができました。

データの更新

次に、作成したデータを更新できるようにしました。
まず、以下のようにurls.pyにコードを追加してルーティングの設定を行いました。

from django.urls import path

from . import views

urlpatterns = [
  path('book/', views.ListBookView.as_view(), name='list-book'),
  path('book/<int:pk>/detail/', views.DetailBookView.as_view(),name='detail-book'),
  path('book/create/', views.CreateBookView.as_view(), name='create-book'),
  path('book/<int:pk>/delete/', views.DeleteBookView.as_view(), name='delete-book'),
  path('book/<int:pk>/update/', views.UpdateBookView.as_view(), name='update-book'),
]

続けて、以下のようにviews.pyにデータ更新用のclassを作成し、テーブルと項目を指定しました。加えて、最後の行では遷移先を設定しました。

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import (
  ListView,
  DetailView,
  CreateView,
  DeleteView,
  UpdateView,
)
from .models import Book


class ListBookView(ListView):
  template_name = 'book/book_list.html'
  model = Book

class DetailBookView(DetailView):
  template_name = 'book/book_detail.html'
  model = Book

class CreateBookView(CreateView):
  template_name = 'book/book_create.html'
  model = Book
  fields = {'title', 'text', 'category'}
  success_url = reverse_lazy('list-book')

class DeleteBookView(DeleteView):
  template_name ='book/book_confirm_delete.html'
  model = Book
  success_url = reverse_lazy('list-book')

class UpdateBookView(UpdateView):
  model = Book
  fields = ('title', 'text', 'category')
  template_name = 'book/book_update.html'
  success_url = reverse_lazy('list-book')

次に、データ更新画面のhtmlファイルbook_update.htmlを以下のように作成しました。

{% extends 'base.html' %}

{% block title %}書籍修正{% endblock %}

{% block content %}
<form method='POST'>{% csrf_token %}
  {{form.as_p}}
  <button type='submit'>修正する</button>
</form>
{% endblock %}

以下の画像は表示されたデータ更新画面の様子です。

上の画像より、データ更新画面が正常に表示されることを確認することができました。
また、データ更新の動作確認は次のブログにまとめています。
nissin-geppox.hatenablog.com

Django Bootstrapの利用とテンプレートの作成

初めに

前回に続いて本棚アプリケーションの作成を行いました。今回はBootstrapによるレイアウトの調整とテンプレートの作成を行いました。
これに対して行った具体的な手順をまとめます。
nissin-geppox.hatenablog.com

概要

  • Bootstrapの導入とレイアウトの変更
  • テンプレートファイルの作成
  • テンプレートの適用

Bootstrapの導入とレイアウトの変更

まず、以下のサイトよりBootstrap導入のためのコードを取得します。
getbootstrap.com
Bootstrap導入のためのコードは以下の通りです。

<!DOCTYPE html>
<html lang="en">

<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.rtl.min.css"
    integrity="sha384-+4j30LffJ4tgIMrq9CwHvn0NjEvmuDCOfk6Rpg2xg7zgOxWWtLtozDEEVvBPgHqE" crossorigin="anonymous">

  <title>任意のタイトル</title>
</head>

上記のコードをBootstrapを導入したいhtmlファイルの先頭に挿入します。
今回は一覧画面のbook_list.htmlに導入しました。
また、レイアウトの調整も行いました。
book_list.htmlは以下のようになりました。

<!DOCTYPE html>
<html lang="en">

<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.rtl.min.css"
    integrity="sha384-+4j30LffJ4tgIMrq9CwHvn0NjEvmuDCOfk6Rpg2xg7zgOxWWtLtozDEEVvBPgHqE" crossorigin="anonymous">

  <title>本棚アプリ</title>
</head>

<body>
  {% for item in object_list %}
  <div class="card">
    <h5 class="card-header">{{ item.title }}</h5>
    <div class="card-body">
      <p class="card-text">{{ item.text }}</p>
      <a href="#" class="btn btn-primary">Go somewhere</a>
      <h6 class="card-title">{{ item.category }}</h6>
    </div>
  </div>
  {% endfor %}
</body>
</html>

この変更により、一覧画面が以下のようになりました。

上の画像より、正常にレイアウトが変更されていることを確認することができました。

テンプレートファイルの作成

上記で導入したBootstrapを他のhtmlファイルでも簡単に利用するためにテンプレートファイルを作成します。
まず、以下のコマンドよりテンプレートを格納するディレクトリとそのファイルを生成しました。

$ mkdir templates
$ touch templates/base.html

続いて、生成したテンプレートファイルbase.htmlを以下のように作成しました。

<!DOCTYPE html>
<html lang="en">

<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.rtl.min.css"
    integrity="sha384-+4j30LffJ4tgIMrq9CwHvn0NjEvmuDCOfk6Rpg2xg7zgOxWWtLtozDEEVvBPgHqE" crossorigin="anonymous">

  <title>{% block title %}{% endblock title %}| 本棚アプリ</title>
</head>

<body>
  {% block content %}{% endblock content %}
</body>

</html>

テンプレートの適用

上記で作成したテンプレートファイルbase.htmlを一覧画面book_list.htmlと詳細画面book_detail.htmlに適応させました。

テンプレートを適応させたbook_list.htmlは以下のようになりました。

{% extends 'base.html' %}

{% block title %}書籍一覧{% endblock %}

{% block content %}
{% for item in object_list %}
<div class="card">
  <h5 class="card-header">{{ item.title }}</h5>
  <div class="card-body">
    <p class="card-text">{{ item.text }}</p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
    <h6 class="card-title">{{ item.category }}</h6>
  </div>
</div>
{% endfor %}
{% endblock content %}

次に、book_detail.htmlは以下のようになりました。

{% extends 'base.html' %}

{% block title %}書籍詳細{% endblock %}

{% block content %}
<div class="card">
  <h5 class="card-header"> {{ object.title }}</h5>
  <div class="card-body">
    <p class="card-text"> {{ object.text }}</p>
    <a href="#" class="btn btn-praimary">ボタン</a>
    <h6 class="card-title">{{ object.category }}</h6>
  </div>
</div>
{% endblock content %}