DjangoのデータベースにPostgreSQLを使用してみよう！！！

## はじめに
 こんにちは。@toamoku-20220418と申します。現在Djangoを使用したアプリケーションの作成を目標に、勉強を進めております。
 そこで今回はDjangoのデータベースにPostgreSQLを使用して、簡単なサンプルアプリを作成しました。PostgreSQLをどのようにして使用すればいいかを解説しておりますので、ぜひ最後までご覧いただき、皆さんの参考になれば幸いです。
## 前提条件
 PCにはpythonがインストールされていることやpythonの文法などの解説は省いております。そして今回は環境変数を使用しますので、私の下記の記事をご覧ください。また使用PCはmacです。

https://qiita.com/toamoku-20220418/items/9419ba85f56c119603ba

## 今回の目標
PostgreSQLをDjangoの環境で使用できるようになること。
## PostgreSQLのインストール
Homebrewでインストールを行いますので、Homebrewがインストールされていない方は下記を参考にしてインストールしてください。

https://brew.sh/ja/

次にHomebrewをアップデートしましょう。
```bash
$ brew --version
$ brew update
$ brew upgrade
```
続いてインストール可能なPostgreSQLを確認し、インストールを行います。
```bash
# インストール可能なPostgreSQLを確認
$ brew search postgresql

# PostgreSQLをインストール
$ brew install postgresql
```
PostgreSQLのバージョンを確認してみましょう。
```bash
$ psql --version
```
## PostgreSQLの設定
最初にPostgreSQLを起動します。
```bash
$ brew services start postgresql
```
データベースに接続してみましょう。
```bash
$ psql postgres
```
最初にデータベースを作成します。
```sql
postgres=# CREATE DATABASE mydatabase;
```
続いてユーザーを作成します。
```sql
postgres=# CREATE USER myuser WITH PASSWORD 'mypassword';
```
作成したユーザーに権限を付与します。
```sql
postgres=# GRANT ALL PRIVILEGES ON DATABASE mydatabase TO myuser;
```
ここで一旦接続を終了しましょう。
```sql
postgres=# \q
```
これで下記の作成を終わりました。
+ データベース：mydatabase
+ ユーザー：myuser
+ 権限：myuserにmydatabaseの操作権限を付与
## Djangoのセットアップ
続いてDjangoのセットアップを行います。
最初にルートディレクトリにプロジェクトディレクトリを作成して、移動します。
```bash
$ mkdir myproject
$ cd myproject
```
仮想環境(virtualenvを使用)を作成して、DjangoとPostgreSQLを使用するためのライブラリをインストールします。環境変数も使用するので、python-dotenvも一緒にインストールします。
```bash
仮想環境の作成
$ virtualenv psqlenv

仮想環境を有効化
$ source psqlenv/bin/activate

Djangoなどをインストール
$ pip install django psycopg2-binary python-dotenv
```
Djangoプロジェクトとアプリの作成を行います。
```bash
$ django-admin manage.py startproject myproject .

$ python manage.py startapp myapp
```
## PostgreSQLの設定
Djangoのデータベース設定を、SQLiteからPostgreSQLに変更します。
```python:settings.py
# myproject/settings.py

import os # 追加
from pathlib import Path
from dotenv import load_dotenv # 追加

load_dotenv()　# 追加

INSTALLED_APPS = [
     :
    'myapp', # 追加
]

# DATABASEを下記に変更
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv('DB_NAME'), # .envから
        'USER': os.getenv('DB_USER'), # .envから
        'PASSWORD': os.getenv('DB_PASSWORD'),　# .envから
        'HOST': 'localhost',  # デフォルトは 'localhost'
        'PORT': '5432',       # デフォルトは '5432'
    }
}

# 定番の変更
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'
```
.envファイルを作成し、にPostgreSQLのユーザー名やパスワードをここで管理します。
```python:.env
# .env

DB_NAME=mydatabase
DB_USER=myuser
DB_PASSWORD=mypassword
```
これでDjangoのデータベースにPostgreSQLを設定することができました。
## PostgreSQLを使用
簡単なアプリを作成して、ちゃんと動いているか確認してみましょう。
最初にモデルを作成します。
```python:models.py
# myapp/models.py

from django.db import models

# Create your models here.
class Book(models.Model):
    title = models.CharField(max_length=30)
    author = models.CharField(max_length=30)
    
    def __str__(self):
        return self.title
```
フォームも作成しちゃいます。
```python:forms.py
# myapp/models.py

from django import forms
from .models import Book # ここを追加

class BookForm(forms.ModelForm):
    
    class Meta:
        model = Book
        fields = ['title', 'author']
```
続いてビューもいっきに作成しちゃいましょう。CRUDの処理をクラスベースビューを使用して作成しちゃいます。
```python:views.py
# myapp/views.py

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

# ホーム画面
def home(request):
    return render(request, 'home.html')

# ListViewで一覧画面
class BookListView(ListView):
    model = Book
    template_name = 'book_list.html'

# DetailViewで詳細画面
class BookDetailView(DetailView):
    model = Book
    template_name = 'book_detail.html'

# CreateViewで新規登録
class BookCreateView(CreateView):
    model = Book
    form_class = BookForm
    template_name = 'book_form.html'
    success_url = reverse_lazy('book-list')

# UpdateViewで編集画面
class BookUpdateView(UpdateView):
    model = Book
    form_class = BookForm
    template_name = 'book_update.html'
    success_url = reverse_lazy('book-list')

# DeleteViewで削除画面
class BookDeleteView(DeleteView):
    model = Book
    template_name = 'book_delete.html'
    success_url = reverse_lazy('book-list')
```
今度はurls.pyを編集します。myprojectとmyappの両方を一度にやっちゃいましょう。
```python:urls.py
# myproject/urls.py

from django.contrib import admin
from django.urls import path, include # include追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('myapp.urls')), # 追加　
]


# myapp/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'), # ホーム
    path('books/', views.BookListView.as_view(), name='book-list'), # リスト
    path('books/<int:pk>/', views.BookDetailView.as_view(), name='book-detail'), # 詳細
    path('books/new/', views.BookCreateView.as_view(), name='book-create'), # 新規登録
    path('books/update/<int:pk>/', views.BookUpdateView.as_view(), name='book-update'), # 編集
    path('books/delete/<int:pk>/', views.BookDeleteView.as_view(), name='book-delete'), # 削除
]
```
templatesディレクトリを作成し、各htmlを作成します。ちょっとだけBootstrapを使用しています。
まずはbase.htmlから
```html:base.html
<!-- templates/base.html -->

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
    <title>{% block title %}{% endblock %}</title>
</head>
<body class="container">
    <nav class="navbar navbar-expand-lg bg-body-tertiary">
        <div class="container-fluid">
            <a class="navbar-brand" href="{% url 'home' %}">BOOKS</a>
            <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 active" aria-current="page" href="{% url 'home' %}">ホーム</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'book-list' %}">リスト</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'book-create' %}">新規登録</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>
    <div class="row">
        <div class="col-md-6 mx-auto">
    {% block content %}
    {% endblock %}
        </div>
    </div>
    <!-- Bootstrap -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
</body>
</html>
```
ホーム画面　home.html
```python:home.html
<!-- templates/home.html -->

{% extends "base.html" %}
{% block title %}home{% endblock %}
{% block content %}
<h1>ホーム画面だよ</h1>
<p>クラスベースビューを使用して、PostgreSQLを使用するよ</p>
<p>サンプルサイトだよ</p>
{% endblock %}
```
リスト表示 book_list.html
```html:book_list.html
<!-- templates/book_list.html -->

{% extends "base.html" %}
{% block title %}Books List{% endblock title %}
{% block content %}
<h1>書籍一覧</h1>
<ul>
    {% for book in object_list %}
    <li><a href="{% url 'book-detail' book.pk %}">{{ book.title }}</a> （著者：{{ book.author }}）</li>
    {% endfor %}
</ul>
<a href="{% url 'book-create' %}">新規登録</a>
{% endblock %}
```
詳細画面　book_detail.html
```html:book_detail.html
<!-- templates/book_detail.html -->

{% extends "base.html" %}
{% block title %}Book Detail{% endblock title %}
{% block content %}
<h1>{{ object.title }}</h1>
<p>著者: {{ object.author }}</p>
<p><a href="{% url 'book-update' book.pk %}">編集はこちら</a></p>
<p><a href="{% url 'book-delete' book.pk %}">削除する</a></p>
<p><a href="{% url 'book-list' %}">一覧へ戻る</a></p>
{% endblock %}
```
新規登録　book_form.html
```html:book_form.html
<!-- templates/book_form.html -->

{% extends "base.html" %}
{% block title %}New Book{% endblock title %}
{% block content %}
<h1>新規登録</h1>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">登録</button>
</form>
<p><a href="{% url 'book-list' %}">一覧へ戻る</a></p>
{% endblock %}
```
編集　book_update.html
```html:book_update.html
<!-- templates/book_update.html -->

{% extends "base.html" %}
{% block title %}Update Book{% endblock title %}
{% block content %}
<h1>編集</h1>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">更新</button>
</form>
<p><a href="{% url 'book-list' %}">一覧へ戻る</a></p>
{% endblock %}
```
削除　book_delete.html
```html:book_delete.html
<!-- templates/book_delete.html -->

{% extends "base.html" %}
{% block title %}Delete Book{% endblock title %}

{% block content %}
<h1>削除確認</h1>
<p>"{{ object.title }}"を削除してもよろしいですか?</p>
<form method="post">
    {% csrf_token %}
    <button type="submit">削除</button>
</form>
<a href="{% url 'book-list' %}">一覧へ戻る</a>
{% endblock %}
```
settings.pyにtemplatesを追加します。
```python:settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'], # ここに「BASE_DIR / 'templates'」を追加
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
```
admin.pyにBookを追加します。
```python:admin.py
# myapp/admin.py

from django.contrib import admin
from .models import Book # 追加

admin.site.register(Book) # 追加
```
管理者サイト用にスーパーユーザーも作成しましょう。
```bash
$ python manage.py createsuperuser
```
ここで必要事項を入力して、スーパーユーザーを作成しましょう。
## 動作確認とデータベース内の確認
実際に動かしてみて、PostgreSQLでデータ処理が行われているかどうか確認してみましょう。
まずはマイグレーションを行います。
```bash
$ python manage.py makemigrations

$ python manage.py migrate
```
続いてサーバーを起動します。
```bash
$ python manage.py runserver
```
最初にブラウザからlocalhost:8000/adminにアクセスします。
そこで管理者サイトにログインし、Bookモデルにデータを作成してみてください。
作成後、再度データベースに接続します。先ほど作成したmyuserでmydatabaseに接続してみましょう。
```bash
$ psql -h localhost -p 5432 -U myuser -d mydatabase
```
-hはホスト名で　-pはポート番号を表します。
続いてテーブルとデータを確認してみましょう。
```sql
mydatabase=# \dt
```
入力すると下記が表示されると思います。
```sql
                  List of relations
 Schema |            Name            | Type  | Owner  
--------+----------------------------+-------+--------
 public | auth_group                 | table | myuser
 public | auth_group_permissions     | table | myuser
 public | auth_permission            | table | myuser
 public | auth_user                  | table | myuser
 public | auth_user_groups           | table | myuser
 public | auth_user_user_permissions | table | myuser
 public | django_admin_log           | table | myuser
 public | django_content_type        | table | myuser
 public | django_migrations          | table | myuser
 public | django_session             | table | myuser
 public | myapp_book                 | table | myuser
 ```
 続いてテーブル内のデータを確認してみます。
 ```sql
 mydatabase=> SELECT * FROM psqlapp_book;
 ```
 SELECT文でmyapp_bookテーブルのデータを全て取得します。
 下記の通りに表示されると思います。
 ```sql
  id |     title      |    author    
 ----+----------------+--------------
   1 | ドラゴンボール 　　　　　　　　| 鳥山　明
  ```
それでは他のCRUD処理も全て実行してみましょう。それで全てうまくいけば、オッケーです。ある程度動かしたら、ターミナルで接続しているデータベースの情報もリスト表示と合っているかなど確認してみるといいと思います。
## 最後に
最後までご覧いただきありがとうございます。今回はPostgreSQLをDjangoで使用してみました。結構色々つまづいたのですが、なんとかうまくいきました。SQL文なども登場することになりましたが、詳しい説明は省いております。何かございましたら、教えていただけると幸いです。またアドバイスなどもぜひお願いいたします。
これからも記事を投稿していきたいと思いますので、引き続きよろしくお願いいたします。